7. Animal data (weight, PFOS, SCFA, pH, transit time)

1 INFO

This document contains the commands necessary to analyse experimental data obtained from “Fibrex” (internal project name: B-02-22), not including 16S-based microbiota data.

2 PROJECT

“Fibrex” refers to an animal study with the scope of investigating the effect of dietary fibres on uptake and wash-out of orally administered perfluorooctane sulfonic acid (PFOS) in adult male rats. The setup of the study is described below including a graphical overview of the setup:

Fibrex study setup
Fibrex study setup
  • 48 adult male Sprague-Dawley rats were acclimatised (7 days prior to study start) to, and continuously fed, either:

    • Research Diet AIN-93G feed (“feed” LF) with no soluble dietary fibres and mineral caseine (serial number D19090404), or
    • Altromin 1314 (“feed” HF), containing dietary fibres.
  • Half of the rats were dosed with corn oil with or without PFOS daily for 7 days by oral gavage (2 mL / kg):

    • 12 rats in each fed group were given pure corn oil as control (“treatment” CTRL)
      • R01-12 for AIN-93G (LF_CTRL)
      • R25-36 for Altromin (HF_CTRL)
    • 12 rats in each fed group were given corn oil with suspended 1.5 mg/mL PFOS (3 mg / kg) (“treatment” PFOS)
      • R13-24 for AIN-93G (LF_PFOS)
      • R37-48 for Altromin (HF_PFOS)
  • Study period run from Day 0 to Day 21:

    • Day 0: Feces samples and first transit time was measured

    • Day 1-7: Dosing period - oral gavage was given daily in the morning (~ 06:00 - 08:00)

      • Body weight was measured daily
      • Day 7: Second transit time measured
    • Day 8: Dissection of half of each group

      • Body weight, feces and urine samples were collected from all animals in the morning (except urine for R19)
      • Blood samples from tongue-vein was taken from all animals not dissected
      • Animals dissected:
        • R01-06
        • R13-18
        • R25-30
        • R37-40 + R43-44
    • Day 8-21: Wash-out period - all remaining rats were measured for PFOS in blood, feces and urine

      • Day 12: Feces samples and body weight
      • Day 16: Feces, urine, tongue-vein blood samples and body weight
      • Day 20: Third transit time measured
    • Day 21: Dissection of remaining 24 rats

      • Body weight, feces and urine samples were collected prior to dissection
      • Animals dissected:
        • R07-12
        • R19-24
        • R31-37
        • R41-42 + R45-48
  • During each dissections following samples and information were collected:

    • Weight: Cecum weight, and Liver weight
    • Blood samples
    • Tissue samples:
      • Liver
      • Brain
    • Gastrointestinal samples from:
      • Upper and lower jejunum
      • Ileum
      • Cecum

2.1 CONTENT

The project data contains:

  • Animal identifiers:

    • “rat_org” lists rat names as given during the study
    • “rat_name” is corrected rat names due to non-continuous numbering for rat_org and dissection days for rat_org R41-R42 (day 21) and R43-R44 (day 8). This column should be used for rat identification - same is corrected in metadata for microbiome data. All following information is following rat_name nomenclature.
    • “cage” number (01-24)
    • “feed” given during the study: LF = no fibre, and HF = fibre
    • “treatment” for oral gavage given: CTRL for control, and PFOS for PFOS
    • “dissection” for dissection days of each rat: d8 and d21
    • combined identifiers with values from above separated by “_“:”feedtreat”, “feedtreatday”
  • Animal weight data (grams) including calculated weights per bw (ratio) and normalized weight data (normalised after mean weight of control group):

    • Body weight (“bw” followed by number of day) from day 0 to day 8 and subsequent days 12, 16, 20 and 21, including bw gain from day 0 - 8, 0 - 21, and 8 - 21.
    • Liver and cecum weight from dissections on day 8 and 21
    • Estimated brain weights per rat (“brain_wt_estimate”) based on “Herculano-Houzel, Mota, and Lent - 2006”
    • Estimated blood volume per rat (“bloodvol_8”, “bloodvol_16”, “bloodvol_21”) based on an standard average of 64mL blood / kilogram in rats “Diehl et al. 2001”
  • Transit time per rat in minutes for:

    • Day 0 (“transit_0”) - note: measurement were done per cage of 2 rats, hence the real value is not known for individual rats
    • Day 7 (“transit_7”)
    • Day 20 (“transit_20”)
  • Total dosed volume (mL) of oral gavage per rat (“dose_total_ml”)

  • PFOS quantitative data in ug / g (“_ugg”) or ug / mL (“_ugml), or mg total (“_mg”) as well as PFOS isomer data:

    • Total dosed PFOS per rat on day 8 (“pfos_total_mg”), respectively
    • Systemic samples:
      • Liver values for day 8 and 21
      • Brain values for day 8 and 21 (except R01, R02, R13, R40, R43, due to missing or loss of sample)
      • Blood serum values for day 8, 16, and 21
    • Secreted / Wash-out samples:
      • Cecum values for day 8 and 21 (except R04)
      • Feces values for day 8, 12, 16, and 21
      • Urine values for day 8, 16, and 21
  • pH measurements of gastrointestinal contents by 4x dilution (wt/vol) in sterile nuclease-free water:

    • Upper and lower jejunum
    • Ileum
    • Cecum (except sample R04)
  • Short-chain fatty acids quantification of 10 compounds in cecal water from day 8 and 21 given in mM:

    • formic acid (formate),
    • acetic acid (acetate),
    • propanoic acid (propionate),
    • 2-methyl-propanoic acid (isobutyrate),
    • butanoic acid (butyrate),
    • 3-methyl-butanoic acid (isovalerate),
    • pentanoic acid (valerate),
    • 4-methyl-pentanoic acid (isocaproate, not included due to low sample count > LOD),
    • hexanoic acid (coproate) and
    • heptanoic acid (enanthate, not included due to low sample count > LOD)
  • Food consumption data from day 0 to 21 based on measurements of feed given and taken away from each cage

The following content of this document goes through data analysis of the above mentioned data and contains code for creation of figures used for the associated publication.

3 SETUP

Following code loads packages, creates necessary folder and saves parameters for the following analyses.

knitr::opts_chunk$set(echo = TRUE)

# Load libraries
library(tidyverse)
library(phyloseq)
library(decontam)
library(pals)
library(ggpubr)
library(vegan)
library(phangorn)
library(kableExtra)
library(plotly)
library(rstatix)
library(forcats)
library(dplyr)
library(tidyr)
library(ggplot2)
library(cowplot)
library(DAtest)
library(ggrepel)

# Save params
saveRDS(params, file = "R_objects/animal_params.RDS")

4 LOAD DATA

Loading data from CSV-format in input folder and saves as Rdata-format.

# Load analysis data
dat <- read.csv(params$input, header = TRUE, sep = ";", dec = ",")

save(dat, file = "R_objects/animal_data.Rdata")

# clear the environment and release memory
rm(list = ls(all.names = TRUE)) #will clear all objects includes hidden objects.
invisible(gc()) #free up memory and report the memory usage.

5 ANIMAL WEIGHT DATA

Animal weight data contains data from body weight through the entire study period with calculated body weight gain, and organ weights from cecum and liver.

5.1 Body weight gain - Day 0-8 (n = 48)

This section will prepare to perform the data analysis for body weight gain

5.1.1 Prepare data

This section sets the variables to be used and prepares the data if necessary.

# load data 
load("R_objects/animal_data.Rdata")
params <- readRDS("R_objects/animal_params.RDS")
dat.clean <- dat

# Set names of variables
PREDICTOR <- "feedtreat"
OUTCOME <- "bw_gain08"
SUBJECT <- "rat_name"

# Create formula
PREDICTOR.F <- ifelse(length(PREDICTOR) > 1, paste(PREDICTOR, collapse = "*"), PREDICTOR)
FORMULA <- as.formula(paste(OUTCOME,PREDICTOR.F, sep = " ~ "))

# Summary samples in groups
dat.clean %>% group_by(across(all_of(PREDICTOR))) %>% get_summary_stats(!!sym(OUTCOME), type = c("mean_sd"))
## # A tibble: 4 × 5
##   feedtreat variable      n  mean    sd
##   <chr>     <fct>     <dbl> <dbl> <dbl>
## 1 HF_CTRL   bw_gain08    12  21.3  3.46
## 2 HF_PFOS   bw_gain08    12  19.4  3.46
## 3 LF_CTRL   bw_gain08    12  24.1  3.54
## 4 LF_PFOS   bw_gain08    12  23.5  3.08
dat.clean %>% group_by(across(all_of(PREDICTOR))) %>% get_summary_stats(!!sym(OUTCOME), type = c("full"))
## # A tibble: 4 × 14
##   feedtreat variable      n   min   max median    q1    q3   iqr   mad  mean
##   <chr>     <fct>     <dbl> <dbl> <dbl>  <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
## 1 HF_CTRL   bw_gain08    12  16.9  26.7   20.2  18.6  24.3  5.69  3.43  21.3
## 2 HF_PFOS   bw_gain08    12  12.8  24.5   19.6  17.8  21.5  3.7   2.96  19.4
## 3 LF_CTRL   bw_gain08    12  18.4  28.7   23.9  21.0  27.2  6.17  4.65  24.1
## 4 LF_PFOS   bw_gain08    12  18.7  27.7   23.7  21.7  25.8  4.09  3.55  23.5
## # ℹ 3 more variables: sd <dbl>, se <dbl>, ci <dbl>
# Create plot
bxp <- dat.clean %>%
  ggboxplot(x = if_else(length(PREDICTOR) > 1, PREDICTOR[2],PREDICTOR[1]),
            y = OUTCOME,
            color = PREDICTOR[1],
            facet.by = if(length(PREDICTOR) == 3) PREDICTOR[3],
            palette = params$COL)
bxp

Assumptions and preliminary tests

The ANOVA tests assume the following characteristics about the data:

  • Independence of the observations. Each subject should belong to only one group. There is no relationship between the observations in each group.
    This is already done for the whole project

  • No significant outliers in the two groups

  • Normality. the data for each group should be approximately normally distributed.

  • Homogeneity of variances. the variance of the outcome variable should be equal in each group.

In this section, we’ll perform some preliminary tests to check whether these assumptions are met.

Identify outliers
Outliers can be easily identified using boxplot methods, implemented in the R function identify_outliers() [rstatix package].

# Test for outliers
dat.clean %>% 
  group_by(across(all_of(PREDICTOR))) %>% 
  identify_outliers(!!sym(OUTCOME))
##  [1] feedtreat         rat_org           rat_name          dissection       
##  [5] cage              treatment         feed              feedtreatday     
##  [9] bw_minus18        bw_0              bw_1              bw_2             
## [13] bw_3              bw_4              bw_5              bw_6             
## [17] bw_7              bw_8              bw_12             bw_16            
## [21] bw_21             bw_gain08         bw_gain021        bw_gain821       
## [25] bloodvol_8        bloodvol_16       bloodvol_21       cecum_wt         
## [29] cecum_wtbw        cecum_norm        liver_wt          liver_wtbw       
## [33] liver_norm        brain_wt_estimate transit_0         transit_7        
## [37] transit_20        dose_total_ml     pfos_total_mg     pfos_liver_ugg   
## [41] pfos_liver_mg     pfos_liver8_pct   pfos_brain_ugg    pfos_brain_uggbw 
## [45] pfos_brain_ug     pfos_brain_pct    pfos_serum8_ugml  pfos_serum8_mg   
## [49] pfos_serum8_pct   pfos_serum16_ugml pfos_serum16_mg   pfos_serum16_pct 
## [53] pfos_serum21_ugml pfos_serum21_mg   pfos_serum21_pct  pfos_feces8_ugg  
## [57] pfos_feces12_ugg  pfos_feces16_ugg  pfos_feces21_ugg  pfos_cecum_ugg   
## [61] pfos_urine8_ugml  pfos_urine16_ugml pfos_urine21_ugml pH_je_up         
## [65] pH_je_down        pH_ileum          pH_cecum          acetic           
## [69] formic            propanoic         m2_propanoic      butanoic         
## [73] m3_butanoic       pentanoic         m4_pentanoic      hexanoic         
## [77] heptanoic         is.outlier        is.extreme       
## <0 rækker> (eller 0-længde row.names)

Data contains two outliers: sample from rat_name R01 and R30.

Check normality
QQ plot and Shapiro-Wilk test of normality are used to analyze the model residuals.

# Build the linear model
model  <- lm(FORMULA, data = dat.clean)
# Create a QQ plot of residuals
ggqqplot(residuals(model))

# Compute Shapiro-Wilk test of normality
shapiro_test(residuals(model))
## # A tibble: 1 × 3
##   variable         statistic p.value
##   <chr>                <dbl>   <dbl>
## 1 residuals(model)     0.961   0.114

Test homogneity of variance assumption
1. The residuals versus fits plot can be used to check the homogeneity of variances.

plot(model, 1)

  1. It’s also possible to use the Levene’s test to check the homogeneity of variances:
dat.clean %>% levene_test(FORMULA)
## Warning in leveneTest.default(y = y, group = group, ...): group coerced to
## factor.
## # A tibble: 1 × 4
##     df1   df2 statistic     p
##   <int> <int>     <dbl> <dbl>
## 1     3    44     0.133 0.940
# Save result
EQUAL.VAR <- dat.clean %>% levene_test(FORMULA) %>% pull(p) > 0.05
## Warning in leveneTest.default(y = y, group = group, ...): group coerced to
## factor.

This shows that body weight gain data has two outliers, has equal variance and is normally distributed without the outliers according to Shapiro-Wilk test. Therefore we use a one-way ANOVA test with Tukey’s honest significance test.

5.1.2 ANOVA One-Way test

Perform test

If we had equality of variance we can now run a one-way ANOVA tests anova_test() (if we have equal variance) or a welch_anova_test() (if variance vary).

if(EQUAL.VAR) {
  res.aov <- dat.clean %>% anova_test(FORMULA)
  res.aov
} else {
  res.aov <- dat.clean %>% welch_anova_test(FORMULA)
  res.aov
}
## ANOVA Table (type II tests)
## 
##      Effect DFn DFd     F     p p<.05   ges
## 1 feedtreat   3  44 4.863 0.005     * 0.249

Perform posthoc test

A significant one-way ANOVA is generally followed up by Tukey post-hoc tests to perform multiple pairwise comparisons between groups. When running relaxed Welch one-way test, the Games-Howell post hoc test or pairwise t-tests (with no assumption of equal variances) can be used to compare all possible combinations of group differences.

if(EQUAL.VAR) {
  pwc <- dat.clean %>% tukey_hsd(FORMULA)
  pwc
} else {
  pwc <- dat.clean %>% games_howell_test(FORMULA)
  pwc
}
## # A tibble: 6 × 9
##   term      group1  group2  null.value estimate conf.low conf.high   p.adj
## * <chr>     <chr>   <chr>        <dbl>    <dbl>    <dbl>     <dbl>   <dbl>
## 1 feedtreat HF_CTRL HF_PFOS          0   -1.88    -5.57       1.82 0.533  
## 2 feedtreat HF_CTRL LF_CTRL          0    2.84    -0.851      6.54 0.184  
## 3 feedtreat HF_CTRL LF_PFOS          0    2.21    -1.49       5.91 0.391  
## 4 feedtreat HF_PFOS LF_CTRL          0    4.72     1.03       8.42 0.00737
## 5 feedtreat HF_PFOS LF_PFOS          0    4.09     0.392      7.78 0.025  
## 6 feedtreat LF_CTRL LF_PFOS          0   -0.634   -4.33       3.06 0.968  
## # ℹ 1 more variable: p.adj.signif <chr>

5.1.3 Create nested figure

# Set variables for inner and outer analysis, and variable for facet
INNER.VAR <- "treatment"
OUTER.VAR <- "feed"
FACETVAR <- "dissection"

# Statistics costumed for facet plotting
## Pairwise comparison for inner variable
stat.in <- dat.clean %>%
  group_by(.data[[OUTER.VAR]]) %>%
  t_test(as.formula(paste(OUTCOME,"~",INNER.VAR, sep = " ")), 
         paired = FALSE, var.equal = EQUAL.VAR, 
         detailed = TRUE, alternative = "two.sided") %>%
  # adjust_pvalue(method = "bonferroni") %>%
  add_significance() %>%
  p_format("p", accuracy = 0.001, trailing.zero = TRUE, new.col = TRUE)
stat.in
## # A tibble: 2 × 18
##   feed  estimate estimate1 estimate2 .y.     group1 group2    n1    n2 statistic
## * <chr>    <dbl>     <dbl>     <dbl> <chr>   <chr>  <chr>  <int> <int>     <dbl>
## 1 HF       1.88       21.3      19.4 bw_gai… CTRL   PFOS      12    12     1.33 
## 2 LF       0.634      24.1      23.5 bw_gai… CTRL   PFOS      12    12     0.468
## # ℹ 8 more variables: p <dbl>, df <dbl>, conf.low <dbl>, conf.high <dbl>,
## #   method <chr>, alternative <chr>, p.signif <chr>, p.format <chr>
## Pairwise comparison for outer variable
stat.out <- dat.clean %>%
  # group_by(.data[[FACETVAR]]) %>%
  t_test(as.formula(paste(OUTCOME,"~", OUTER.VAR, sep = " ")), 
         paired = FALSE, var.equal = EQUAL.VAR, 
         detailed = TRUE, alternative = "two.sided") %>%
  # adjust_pvalue(method = "bonferroni") %>%
  add_significance() %>%
  p_format("p", accuracy = 0.001, trailing.zero = TRUE, new.col = TRUE)
stat.out
## # A tibble: 1 × 17
##   estimate estimate1 estimate2 .y.    group1 group2    n1    n2 statistic      p
## *    <dbl>     <dbl>     <dbl> <chr>  <chr>  <chr>  <int> <int>     <dbl>  <dbl>
## 1    -3.47      20.3      23.8 bw_ga… HF     LF        24    24     -3.54 9.3e-4
## # ℹ 7 more variables: df <dbl>, conf.low <dbl>, conf.high <dbl>, method <chr>,
## #   alternative <chr>, p.signif <chr>, p.format <chr>
## Calculate positions statistics on plot
stat.in <- stat.in %>% add_xy_position(x = OUTER.VAR, dodge = 0.8)
stat.out <- stat.out %>% add_xy_position(x = OUTER.VAR)
stat.out$y.position <- max(stat.in$y.position)*1.1

# Sort dat.clean
dat.clean <- dat.clean[order(dat.clean$feedtreat),]

# Create plot
p <- ggboxplot(dat.clean, x = OUTER.VAR, y = OUTCOME,
               color = PREDICTOR,
               fill = PREDICTOR, 
               add =  "jitter", 
               add.params = list(size = 1)) +
  scale_fill_manual(values = params$COL1, name = "Group", labels = c("HF_CTRL" = "HF-CTRL","HF_PFOS" = "HF-PFOS","LF_CTRL" = "LF-CTRL","LF_PFOS" = "LF-PFOS")) +
  scale_color_manual(values = c("black","black","black","black")) +
  scale_y_continuous(name = "Bodyweight gain",limits = c(10,35),breaks = seq(10,35,5), labels = function(x) paste0(x, "%")) +
  guides(color = "none") +
  theme(axis.title.x = element_blank())

p <- p + stat_pvalue_manual(stat.in, label = "p.format",tip.length = 0, hide.ns = FALSE)+
  stat_pvalue_manual(stat.out, label = "p.format", tip.length = 0, hide.ns = FALSE)
p

# Plot for saving without legend
p2 <- p + theme(legend.position = "none")

# Output plot
ggsave(filename = paste0("plots/animal_data/weight/",OUTCOME,"_plot.png"), p2, device = "png", dpi = 300, units = "mm", width = 100, height = 100)
ggsave(filename = paste0("plots/animal_data/weight/",OUTCOME,"_plot.pdf"), p2, device = "pdf", dpi = 300, units = "mm", width = 100, height = 100)

# clear the environment and release memory
rm(list = ls(all.names = TRUE)) #will clear all objects includes hidden objects.
invisible(gc()) #free up memory and report the memory usage.

5.2 Body weight gain - Day 0-21 (n = 24)

This section will prepare to perform the data analysis for body weight gain

5.2.1 Prepare data

This section sets the variables to be used and prepares the data if necessary.

# load data 
load("R_objects/animal_data.Rdata")
params <- readRDS("R_objects/animal_params.RDS")

# Subset
dat.clean <- subset(dat, !is.na(bw_gain021))

# Set names of variables
PREDICTOR <- "feedtreat"
OUTCOME <- "bw_gain021"
SUBJECT <- "rat_name"

# Create formula
PREDICTOR.F <- ifelse(length(PREDICTOR) > 1, paste(PREDICTOR, collapse = "*"), PREDICTOR)
FORMULA <- as.formula(paste(OUTCOME,PREDICTOR.F, sep = " ~ "))

# Summary samples in groups
dat.clean %>% group_by(across(all_of(PREDICTOR))) %>% get_summary_stats(!!sym(OUTCOME), type = "mean_sd")
## # A tibble: 4 × 5
##   feedtreat variable       n  mean    sd
##   <chr>     <fct>      <dbl> <dbl> <dbl>
## 1 HF_CTRL   bw_gain021     6  45.5  6.86
## 2 HF_PFOS   bw_gain021     6  47.4  5.93
## 3 LF_CTRL   bw_gain021     6  57.0  7.82
## 4 LF_PFOS   bw_gain021     6  53.0  5.48
dat.clean %>% group_by(across(all_of(PREDICTOR))) %>% get_summary_stats(!!sym(OUTCOME), type = c("full"))
## # A tibble: 4 × 14
##   feedtreat variable       n   min   max median    q1    q3   iqr   mad  mean
##   <chr>     <fct>      <dbl> <dbl> <dbl>  <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
## 1 HF_CTRL   bw_gain021     6  36.5  54.6   46.8  40.3  49.3  8.92  7.71  45.5
## 2 HF_PFOS   bw_gain021     6  36.6  52.1   49.5  45.7  51.3  5.58  3.67  47.4
## 3 LF_CTRL   bw_gain021     6  48.9  68.1   54.3  51.6  62.7 11.1   6.40  57.0
## 4 LF_PFOS   bw_gain021     6  47.0  60.8   53.1  48.2  56.2  8.03  7.24  53.0
## # ℹ 3 more variables: sd <dbl>, se <dbl>, ci <dbl>
# Create plot
bxp <- dat.clean %>%
  ggboxplot(x = if_else(length(PREDICTOR) > 1, PREDICTOR[2],PREDICTOR[1]),
            y = OUTCOME,
            color = PREDICTOR[1],
            facet.by = if(length(PREDICTOR) == 3) PREDICTOR[3],
            palette = params$COL)
bxp

# Test for outliers
dat.clean %>% 
  group_by(across(all_of(PREDICTOR))) %>% 
  identify_outliers(!!sym(OUTCOME))
## # A tibble: 1 × 79
##   feedtreat rat_org rat_name dissection  cage treatment feed  feedtreatday
##   <chr>     <chr>   <chr>    <chr>      <int> <chr>     <chr> <chr>       
## 1 HF_PFOS   R46     R46      d21           23 PFOS      HF    HF_PFOS_d21 
## # ℹ 71 more variables: bw_minus18 <dbl>, bw_0 <dbl>, bw_1 <int>, bw_2 <int>,
## #   bw_3 <int>, bw_4 <int>, bw_5 <int>, bw_6 <int>, bw_7 <int>, bw_8 <int>,
## #   bw_12 <dbl>, bw_16 <dbl>, bw_21 <dbl>, bw_gain08 <dbl>, bw_gain021 <dbl>,
## #   bw_gain821 <dbl>, bloodvol_8 <dbl>, bloodvol_16 <dbl>, bloodvol_21 <dbl>,
## #   cecum_wt <dbl>, cecum_wtbw <dbl>, cecum_norm <dbl>, liver_wt <dbl>,
## #   liver_wtbw <dbl>, liver_norm <dbl>, brain_wt_estimate <dbl>,
## #   transit_0 <int>, transit_7 <int>, transit_20 <int>, dose_total_ml <dbl>, …
# Build the linear model
model  <- lm(FORMULA, data = dat.clean)
# Create a QQ plot of residuals
ggqqplot(residuals(model))

# Compute Shapiro-Wilk test of normality
shapiro_test(residuals(model))
## # A tibble: 1 × 3
##   variable         statistic p.value
##   <chr>                <dbl>   <dbl>
## 1 residuals(model)     0.969   0.636
plot(model, 1)

dat.clean %>% levene_test(FORMULA)
## Warning in leveneTest.default(y = y, group = group, ...): group coerced to
## factor.
## # A tibble: 1 × 4
##     df1   df2 statistic     p
##   <int> <int>     <dbl> <dbl>
## 1     3    20     0.302 0.824
# Save result
EQUAL.VAR <- dat.clean %>% levene_test(FORMULA) %>% pull(p) > 0.05
## Warning in leveneTest.default(y = y, group = group, ...): group coerced to
## factor.

This shows that body weight gain data has two outliers, has equal variance and is normally distributed without the outliers according to Shapiro-Wilk test. Therefore we use a one-way ANOVA test with Tukey’s honest significance test.

5.2.2 ANOVA One-Way test

Perform test

If we had equality of variance we can now run a one-way ANOVA tests anova_test() (if we have equal variance) or a welch_anova_test() (if variance vary).

if(EQUAL.VAR) {
  res.aov <- dat.clean %>% anova_test(FORMULA)
  res.aov
} else {
  res.aov <- dat.clean %>% welch_anova_test(FORMULA)
  res.aov
}
## ANOVA Table (type II tests)
## 
##      Effect DFn DFd     F     p p<.05   ges
## 1 feedtreat   3  20 3.817 0.026     * 0.364

Perform posthoc test

A significant one-way ANOVA is generally followed up by Tukey post-hoc tests to perform multiple pairwise comparisons between groups. When running relaxed Welch one-way test, the Games-Howell post hoc test or pairwise t-tests (with no assumption of equal variances) can be used to compare all possible combinations of group differences.

if(EQUAL.VAR) {
  pwc <- dat.clean %>% tukey_hsd(FORMULA)
  pwc
} else {
  pwc <- dat.clean %>% games_howell_test(FORMULA)
  pwc
}
## # A tibble: 6 × 9
##   term  group1 group2 null.value estimate conf.low conf.high  p.adj p.adj.signif
## * <chr> <chr>  <chr>       <dbl>    <dbl>    <dbl>     <dbl>  <dbl> <chr>       
## 1 feed… HF_CT… HF_PF…          0     1.86   -8.78      12.5  0.961  ns          
## 2 feed… HF_CT… LF_CT…          0    11.5     0.838     22.1  0.0316 *           
## 3 feed… HF_CT… LF_PF…          0     7.46   -3.18      18.1  0.235  ns          
## 4 feed… HF_PF… LF_CT…          0     9.62   -1.02      20.3  0.0853 ns          
## 5 feed… HF_PF… LF_PF…          0     5.61   -5.04      16.2  0.471  ns          
## 6 feed… LF_CT… LF_PF…          0    -4.02  -14.7        6.63 0.719  ns

5.2.3 Create nested figure

# Set variables for inner and outer analysis, and variable for facet
INNER.VAR <- "treatment"
OUTER.VAR <- "feed"
FACETVAR <- "dissection"

# Statistics costumed for facet plotting
## Pairwise comparison for inner variable
stat.in <- dat.clean %>%
  group_by(.data[[OUTER.VAR]]) %>%
  t_test(as.formula(paste(OUTCOME,"~",INNER.VAR, sep = " ")), 
         paired = FALSE, var.equal = EQUAL.VAR, 
         detailed = TRUE, alternative = "two.sided") %>%
  # adjust_pvalue(method = "bonferroni") %>%
  add_significance() %>%
  p_format("p", accuracy = 0.001, trailing.zero = TRUE, new.col = TRUE)
stat.in
## # A tibble: 2 × 18
##   feed  estimate estimate1 estimate2 .y.     group1 group2    n1    n2 statistic
## * <chr>    <dbl>     <dbl>     <dbl> <chr>   <chr>  <chr>  <int> <int>     <dbl>
## 1 HF       -1.86      45.5      47.4 bw_gai… CTRL   PFOS       6     6    -0.502
## 2 LF        4.02      57.0      53.0 bw_gai… CTRL   PFOS       6     6     1.03 
## # ℹ 8 more variables: p <dbl>, df <dbl>, conf.low <dbl>, conf.high <dbl>,
## #   method <chr>, alternative <chr>, p.signif <chr>, p.format <chr>
## Pairwise comparison for outer variable
stat.out <- dat.clean %>%
  # group_by(.data[[FACETVAR]]) %>%
  t_test(as.formula(paste(OUTCOME,"~", OUTER.VAR, sep = " ")), 
         paired = FALSE, var.equal = EQUAL.VAR, 
         detailed = TRUE, alternative = "two.sided") %>%
  # adjust_pvalue(method = "bonferroni") %>%
  add_significance() %>%
  p_format("p", accuracy = 0.001, trailing.zero = TRUE, new.col = TRUE)
stat.out
## # A tibble: 1 × 17
##   estimate estimate1 estimate2 .y.   group1 group2    n1    n2 statistic       p
## *    <dbl>     <dbl>     <dbl> <chr> <chr>  <chr>  <int> <int>     <dbl>   <dbl>
## 1    -8.54      46.4      55.0 bw_g… HF     LF        12    12     -3.23 0.00389
## # ℹ 7 more variables: df <dbl>, conf.low <dbl>, conf.high <dbl>, method <chr>,
## #   alternative <chr>, p.signif <chr>, p.format <chr>
## Calculate positions statistics on plot
stat.in <- stat.in %>% add_xy_position(x = OUTER.VAR, dodge = 0.8)
stat.out <- stat.out %>% add_xy_position(x = OUTER.VAR)
stat.out$y.position <- max(stat.in$y.position)*1.1

# Sort dat.clean
dat.clean <- dat.clean[order(dat.clean$feedtreat),]

# Create plot
p <- ggboxplot(dat.clean, x = OUTER.VAR, y = OUTCOME,
               color = PREDICTOR,
               fill = PREDICTOR, 
               add =  "jitter", 
               add.params = list(size = 1)) +
  scale_fill_manual(values = params$COL1, name = "Group", labels = c("HF_CTRL" = "HF-CTRL","HF_PFOS" = "HF-PFOS","LF_CTRL" = "LF-CTRL","LF_PFOS" = "LF-PFOS")) +
  scale_color_manual(values = c("black","black","black","black")) +
  scale_y_continuous(name = "Bodyweight gain",limits = c(30,80),breaks = seq(30,80,10), labels = function(x) paste0(x, "%")) +
  guides(color = "none") +
  theme(axis.title.x = element_blank())

p <- p + stat_pvalue_manual(stat.in, label = "p.format",tip.length = 0, hide.ns = FALSE)+
  stat_pvalue_manual(stat.out, label = "p.format", tip.length = 0, hide.ns = FALSE)

p

# Plot for saving without legend
p2 <- p + theme(legend.position = "none")

# Output plot
ggsave(filename = paste0("plots/animal_data/weight/",OUTCOME,"_plot.png"), p2, device = "png", dpi = 300, units = "mm", width = 100, height = 100)
ggsave(filename = paste0("plots/animal_data/weight/",OUTCOME,"_plot.pdf"), p2, device = "pdf", dpi = 300, units = "mm", width = 100, height = 100)

# clear the environment and release memory
rm(list = ls(all.names = TRUE)) #will clear all objects includes hidden objects.
invisible(gc()) #free up memory and report the memory usage.

5.3 Body weight gain - Day 8-21 (n = 24)

This section will prepare to perform the data analysis for body weight gain

5.3.1 Prepare data

This section sets the variables to be used and prepares the data if necessary.

# load data 
load("R_objects/animal_data.Rdata")
params <- readRDS("R_objects/animal_params.RDS")

# Subset
dat.clean <- subset(dat, !is.na(bw_gain821))

# Set names of variables
PREDICTOR <- "feedtreat"
OUTCOME <- "bw_gain821"
SUBJECT <- "rat_name"

# Create formula
PREDICTOR.F <- ifelse(length(PREDICTOR) > 1, paste(PREDICTOR, collapse = "*"), PREDICTOR)
FORMULA <- as.formula(paste(OUTCOME,PREDICTOR.F, sep = " ~ "))

# Summary samples in groups
dat.clean %>% group_by(across(all_of(PREDICTOR))) %>% get_summary_stats(!!sym(OUTCOME), type = "mean_sd")
## # A tibble: 4 × 5
##   feedtreat variable       n  mean    sd
##   <chr>     <fct>      <dbl> <dbl> <dbl>
## 1 HF_CTRL   bw_gain821     6  19.5  4.42
## 2 HF_PFOS   bw_gain821     6  22.0  5.08
## 3 LF_CTRL   bw_gain821     6  25.2  4.55
## 4 LF_PFOS   bw_gain821     6  23.9  4.47
# Create plot
bxp <- dat.clean %>%
  ggboxplot(x = if_else(length(PREDICTOR) > 1, PREDICTOR[2],PREDICTOR[1]),
            y = OUTCOME,
            color = PREDICTOR[1],
            facet.by = if(length(PREDICTOR) == 3) PREDICTOR[3],
            palette = params$COL)
bxp

# Test for outliers
dat.clean %>% 
  group_by(across(all_of(PREDICTOR))) %>% 
  identify_outliers(!!sym(OUTCOME))
## # A tibble: 4 × 79
##   feedtreat rat_org rat_name dissection  cage treatment feed  feedtreatday
##   <chr>     <chr>   <chr>    <chr>      <int> <chr>     <chr> <chr>       
## 1 HF_CTRL   R36     R36      d21           18 CTRL      HF    HF_CTRL_d21 
## 2 HF_PFOS   R46     R46      d21           23 PFOS      HF    HF_PFOS_d21 
## 3 LF_PFOS   R19     R19      d21           10 PFOS      LF    LF_PFOS_d21 
## 4 LF_PFOS   R23     R23      d21           12 PFOS      LF    LF_PFOS_d21 
## # ℹ 71 more variables: bw_minus18 <dbl>, bw_0 <dbl>, bw_1 <int>, bw_2 <int>,
## #   bw_3 <int>, bw_4 <int>, bw_5 <int>, bw_6 <int>, bw_7 <int>, bw_8 <int>,
## #   bw_12 <dbl>, bw_16 <dbl>, bw_21 <dbl>, bw_gain08 <dbl>, bw_gain021 <dbl>,
## #   bw_gain821 <dbl>, bloodvol_8 <dbl>, bloodvol_16 <dbl>, bloodvol_21 <dbl>,
## #   cecum_wt <dbl>, cecum_wtbw <dbl>, cecum_norm <dbl>, liver_wt <dbl>,
## #   liver_wtbw <dbl>, liver_norm <dbl>, brain_wt_estimate <dbl>,
## #   transit_0 <int>, transit_7 <int>, transit_20 <int>, dose_total_ml <dbl>, …
# Build the linear model
model  <- lm(FORMULA, data = dat.clean)
# Create a QQ plot of residuals
ggqqplot(residuals(model))

# Compute Shapiro-Wilk test of normality
shapiro_test(residuals(model))
## # A tibble: 1 × 3
##   variable         statistic p.value
##   <chr>                <dbl>   <dbl>
## 1 residuals(model)     0.955   0.348
plot(model, 1)

dat.clean %>% levene_test(FORMULA)
## Warning in leveneTest.default(y = y, group = group, ...): group coerced to
## factor.
## # A tibble: 1 × 4
##     df1   df2 statistic     p
##   <int> <int>     <dbl> <dbl>
## 1     3    20    0.0776 0.971
# Save result
EQUAL.VAR <- dat.clean %>% levene_test(FORMULA) %>% pull(p) > 0.05
## Warning in leveneTest.default(y = y, group = group, ...): group coerced to
## factor.

This shows that body weight gain data has two outliers, has equal variance and is normally distributed without the outliers according to Shapiro-Wilk test. Therefore we use a one-way ANOVA test with Tukey’s honest significance test.

5.3.2 ANOVA One-Way test

Perform test

If we had equality of variance we can now run a one-way ANOVA tests anova_test() (if we have equal variance) or a welch_anova_test() (if variance vary).

if(EQUAL.VAR) {
  res.aov <- dat.clean %>% anova_test(FORMULA)
  res.aov
} else {
  res.aov <- dat.clean %>% welch_anova_test(FORMULA)
  res.aov
}
## ANOVA Table (type II tests)
## 
##      Effect DFn DFd     F     p p<.05   ges
## 1 feedtreat   3  20 1.716 0.196       0.205

Perform posthoc test

A significant one-way ANOVA is generally followed up by Tukey post-hoc tests to perform multiple pairwise comparisons between groups. When running relaxed Welch one-way test, the Games-Howell post hoc test or pairwise t-tests (with no assumption of equal variances) can be used to compare all possible combinations of group differences.

if(EQUAL.VAR) {
  pwc <- dat.clean %>% tukey_hsd(FORMULA)
  pwc
} else {
  pwc <- dat.clean %>% games_howell_test(FORMULA)
  pwc
}
## # A tibble: 6 × 9
##   term   group1 group2 null.value estimate conf.low conf.high p.adj p.adj.signif
## * <chr>  <chr>  <chr>       <dbl>    <dbl>    <dbl>     <dbl> <dbl> <chr>       
## 1 feedt… HF_CT… HF_PF…          0     2.51    -4.99     10.0  0.786 ns          
## 2 feedt… HF_CT… LF_CT…          0     5.71    -1.79     13.2  0.177 ns          
## 3 feedt… HF_CT… LF_PF…          0     4.40    -3.09     11.9  0.378 ns          
## 4 feedt… HF_PF… LF_CT…          0     3.20    -4.29     10.7  0.637 ns          
## 5 feedt… HF_PF… LF_PF…          0     1.89    -5.60      9.39 0.893 ns          
## 6 feedt… LF_CT… LF_PF…          0    -1.31    -8.80      6.19 0.961 ns

5.3.3 Create figure

# Set variables for inner and outer analysis, and variable for facet
INNER.VAR <- "treatment"
OUTER.VAR <- "feed"
FACETVAR <- "dissection"

# Statistics costumed for facet plotting
## Pairwise comparison for inner variable
stat.in <- dat.clean %>%
  group_by(.data[[OUTER.VAR]]) %>%
  t_test(as.formula(paste(OUTCOME,"~",INNER.VAR, sep = " ")), 
         paired = FALSE, var.equal = EQUAL.VAR, 
         detailed = TRUE, alternative = "two.sided") %>%
  # adjust_pvalue(method = "bonferroni") %>%
  add_significance() %>%
  p_format("p", accuracy = 0.001, trailing.zero = TRUE, new.col = TRUE)
stat.in
## # A tibble: 2 × 18
##   feed  estimate estimate1 estimate2 .y.     group1 group2    n1    n2 statistic
## * <chr>    <dbl>     <dbl>     <dbl> <chr>   <chr>  <chr>  <int> <int>     <dbl>
## 1 HF       -2.51      19.5      22.0 bw_gai… CTRL   PFOS       6     6    -0.912
## 2 LF        1.31      25.2      23.9 bw_gai… CTRL   PFOS       6     6     0.502
## # ℹ 8 more variables: p <dbl>, df <dbl>, conf.low <dbl>, conf.high <dbl>,
## #   method <chr>, alternative <chr>, p.signif <chr>, p.format <chr>
## Pairwise comparison for outer variable
stat.out <- dat.clean %>%
  # group_by(.data[[FACETVAR]]) %>%
  t_test(as.formula(paste(OUTCOME,"~", OUTER.VAR, sep = " ")), 
         paired = FALSE, var.equal = EQUAL.VAR, 
         detailed = TRUE, alternative = "two.sided") %>%
  # adjust_pvalue(method = "bonferroni") %>%
  add_significance() %>%
  p_format("p", accuracy = 0.001, trailing.zero = TRUE, new.col = TRUE)
stat.out
## # A tibble: 1 × 17
##   estimate estimate1 estimate2 .y.    group1 group2    n1    n2 statistic      p
## *    <dbl>     <dbl>     <dbl> <chr>  <chr>  <chr>  <int> <int>     <dbl>  <dbl>
## 1    -3.80      20.8      24.6 bw_ga… HF     LF        12    12     -2.05 0.0525
## # ℹ 7 more variables: df <dbl>, conf.low <dbl>, conf.high <dbl>, method <chr>,
## #   alternative <chr>, p.signif <chr>, p.format <chr>
## Calculate positions statistics on plot
stat.in <- stat.in %>% add_xy_position(x = OUTER.VAR, dodge = 0.8)
stat.out <- stat.out %>% add_xy_position(x = OUTER.VAR)
stat.out$y.position <- max(stat.in$y.position)*1.1

# Sort dat.clean
dat.clean <- dat.clean[order(dat.clean$feedtreat),]

# Create plot
p <- ggboxplot(dat.clean, x = OUTER.VAR, y = OUTCOME,
               color = PREDICTOR,
               fill = PREDICTOR, 
               add =  "jitter", 
               add.params = list(size = 1)) +
  scale_fill_manual(values = params$COL1, name = "Group", labels = c("HF_CTRL" = "HF-CTRL","HF_PFOS" = "HF-PFOS","LF_CTRL" = "LF-CTRL","LF_PFOS" = "LF-PFOS")) +
  scale_color_manual(values = c("black","black","black","black")) +
  scale_y_continuous(name = "Bodyweight gain",limits = c(10,40),breaks = seq(10,40,5), labels = function(x) paste0(x, "%")) +
  guides(color = "none") +
  theme(axis.title.x = element_blank())

p <- p + stat_pvalue_manual(stat.in, label = "p.format",tip.length = 0, hide.ns = FALSE)+
  stat_pvalue_manual(stat.out, label = "p.format", tip.length = 0, hide.ns = FALSE)

p

# Plot for saving without legend
p2 <- p + theme(legend.position = "none")

# Output plot
ggsave(filename = paste0("plots/animal_data/weight/",OUTCOME,"_plot.png"), p2, device = "png", dpi = 300, units = "mm", width = 100, height = 100)
ggsave(filename = paste0("plots/animal_data/weight/",OUTCOME,"_plot.pdf"), p2, device = "pdf", dpi = 300, units = "mm", width = 100, height = 100)

# clear the environment and release memory
rm(list = ls(all.names = TRUE)) #will clear all objects includes hidden objects.
invisible(gc()) #free up memory and report the memory usage.

5.4 Body weight over time

This section will prepare data analysis and illustration of body weight data per rat over the course of this study. Final plot shows a normalised weight over time (bw day x / bw day 0) while rate of growth is calculated based on true weight data to produce a rate of g/day.

5.4.1 Prepare data and growth rates

# Load data
load("R_objects/animal_data.Rdata")
params <- readRDS("R_objects/animal_params.RDS")

# Set parameters
PREDICTOR <- c("bw_time_n","treatment","feed")
OUTCOME <- "wt_n"
SUBJECT <- "rat_name"

# Create data frame for data representation
dat.clean <- dat %>% select(rat_name, feed, treatment, feedtreat, dissection, bw_0, bw_1, bw_2, bw_3, bw_4, bw_5, bw_6, bw_7, bw_8, bw_12, bw_16, bw_21)

# Normalise data
dat.clean <- dat.clean %>% mutate(bw_0_n = bw_0 / bw_0, bw_1_n = bw_1 / bw_0, bw_2_n = bw_2 / bw_0, bw_3_n = bw_3 / bw_0, bw_4_n = bw_4 / bw_0, bw_5_n = bw_5 / bw_0, bw_6_n = bw_6 / bw_0, bw_7_n = bw_7 / bw_0, bw_8_n = bw_8 / bw_0, bw_12_n = bw_12 / bw_0, bw_16_n = bw_16 / bw_0, bw_21_n = bw_21 / bw_0)

# Pivot longer
dat.bwn <- dat.clean %>%
    pivot_longer(., cols = c(bw_0_n, bw_1_n, bw_2_n, bw_3_n, bw_4_n, bw_5_n, bw_6_n, bw_7_n, bw_8_n, bw_12_n, bw_16_n, bw_21_n), names_to = "bw_time_n", values_to = "wt_n")

dat.bw <- dat.clean %>% 
  pivot_longer(., cols = c(bw_0, bw_1, bw_2, bw_3, bw_4, bw_5, bw_6, bw_7, bw_8, bw_12, bw_16, bw_21), names_to = "bw_time", values_to = "wt")

# Create column for day of sampling (numeric)
dat.bwn <- dat.bwn %>% mutate("day" = case_when(bw_time_n == "bw_0_n" ~ 0,
                                                    bw_time_n == "bw_1_n" ~ 1,
                                                    bw_time_n == "bw_2_n" ~ 2,
                                                    bw_time_n == "bw_3_n" ~ 3,
                                                    bw_time_n == "bw_4_n" ~ 4,
                                                    bw_time_n == "bw_5_n" ~ 5,
                                                    bw_time_n == "bw_6_n" ~ 6,
                                                    bw_time_n == "bw_7_n" ~ 7,
                                                    bw_time_n == "bw_8_n" ~ 8,
                                                    bw_time_n == "bw_12_n" ~ 12,
                                                    bw_time_n == "bw_16_n" ~ 16,
                                                    bw_time_n == "bw_21_n" ~ 21))

# Create column for day of sampling (numeric)
dat.bw <- dat.bw %>% mutate("day" = case_when(bw_time == "bw_0" ~ 0,
                                                    bw_time == "bw_1" ~ 1,
                                                    bw_time == "bw_2" ~ 2,
                                                    bw_time == "bw_3" ~ 3,
                                                    bw_time == "bw_4" ~ 4,
                                                    bw_time == "bw_5" ~ 5,
                                                    bw_time == "bw_6" ~ 6,
                                                    bw_time == "bw_7" ~ 7,
                                                    bw_time == "bw_8" ~ 8,
                                                    bw_time == "bw_12" ~ 12,
                                                    bw_time == "bw_16" ~ 16,
                                                    bw_time == "bw_21" ~ 21))
# Order dataframe for analysis
dat.bwn <- dat.bwn[order(dat.bw$day),]

# Remove rows with NA
dat.bwn <- subset(dat.bwn, !is.na(wt_n))
dat.bw <- subset(dat.bw, !is.na(wt))

# Calculate regression lines per treatment group
reg_lines <- dat.bw %>%
  group_by(feedtreat) %>%
  summarize(slope = coef(lm(wt ~ day))[2],
            intercept = coef(lm(wt ~ day))[1])

# Calculate regression lines per rat over time
rl_rats <- dat.bw %>%
  group_by(rat_name) %>%
  summarize(slope = coef(lm(wt ~ day))[2],
            intercept = coef(lm(wt ~ day))[1]) %>%
  cbind(data.frame(feedtreat = rep(c("LF_CTRL","LF_PFOS","HF_CTRL","HF_PFOS"), each = 12),
                   feed = rep(c("LF","HF"), each = 24),
                   treatment = rep(c("CTRL","PFOS"), each = 12, times = 2),
                   day = rep(c("d8","d21"), each = 6, times = 4)))

# Build the linear model
model  <- lm(slope ~ feedtreat, data = rl_rats)
# Create a QQ plot of residuals
ggqqplot(residuals(model))

# Compute Shapiro-Wilk test of normality
shapiro_test(residuals(model))
## # A tibble: 1 × 3
##   variable         statistic p.value
##   <chr>                <dbl>   <dbl>
## 1 residuals(model)     0.952  0.0472
# Test for equal variance
rl_rats %>% levene_test(slope ~ feedtreat)
## Warning in leveneTest.default(y = y, group = group, ...): group coerced to
## factor.
## # A tibble: 1 × 4
##     df1   df2 statistic     p
##   <int> <int>     <dbl> <dbl>
## 1     3    44     0.220 0.882
# Save result
EQUAL.VAR <- rl_rats %>% levene_test(slope ~ feedtreat) %>% pull(p) > 0.05
## Warning in leveneTest.default(y = y, group = group, ...): group coerced to
## factor.
# Kruskal-Wallis test
pwc1 <- rl_rats %>%
  kruskal_test(slope ~ feedtreat)
pwc1
## # A tibble: 1 × 6
##   .y.       n statistic    df      p method        
## * <chr> <int>     <dbl> <int>  <dbl> <chr>         
## 1 slope    48      7.49     3 0.0579 Kruskal-Wallis
# Posthoc Dunn's test
pwc2 <- rl_rats %>% 
  dunn_test(slope ~ feedtreat, p.adjust.method = "fdr") 
pwc2
## # A tibble: 6 × 9
##   .y.   group1  group2     n1    n2 statistic      p p.adj p.adj.signif
## * <chr> <chr>   <chr>   <int> <int>     <dbl>  <dbl> <dbl> <chr>       
## 1 slope HF_CTRL HF_PFOS    12    12    -0.190 0.850  0.850 ns          
## 2 slope HF_CTRL LF_CTRL    12    12     2.01  0.0442 0.133 ns          
## 3 slope HF_CTRL LF_PFOS    12    12     1.62  0.106  0.158 ns          
## 4 slope HF_PFOS LF_CTRL    12    12     2.20  0.0277 0.133 ns          
## 5 slope HF_PFOS LF_PFOS    12    12     1.81  0.0706 0.141 ns          
## 6 slope LF_CTRL LF_PFOS    12    12    -0.394 0.694  0.833 ns

5.4.2 Statistical analysis

Based on the above tests of the regression for four groups we can analyse the data using Kruskal-Wallis with posthoc Dunn’s test.

# Set variables for inner and outer analysis, and variable for facet
INNER.VAR <- "treatment"
OUTER.VAR <- "feed"
FACETVAR <- "day"

# Statistics for overall data
stat.in2 <- rl_rats %>%
  group_by(.data[[OUTER.VAR]]) %>%
  wilcox_test(as.formula(paste("slope ~",INNER.VAR, sep = " "))) %>%
  adjust_pvalue(method = "BH") %>%
  add_significance("p.adj") %>% 
  p_format("p.adj", accuracy = 0.0001, trailing.zero = TRUE, new.col = TRUE)
stat.in2
## # A tibble: 2 × 11
##   feed  .y.   group1 group2    n1    n2 statistic     p p.adj p.adj.signif
## * <chr> <chr> <chr>  <chr>  <int> <int>     <dbl> <dbl> <dbl> <chr>       
## 1 HF    slope CTRL   PFOS      12    12        73 0.977 0.977 ns          
## 2 LF    slope CTRL   PFOS      12    12        79 0.713 0.977 ns          
## # ℹ 1 more variable: p.adj.format <chr>
stat.out2 <- rl_rats %>%
  wilcox_test(as.formula(paste("slope ~",OUTER.VAR, sep = " "))) %>%
  adjust_pvalue(method = "BH") %>%
  add_significance("p.adj") %>%
  p_format("p.adj", accuracy = 0.0001, trailing.zero = TRUE, new.col = TRUE)
stat.out2
## # A tibble: 1 × 10
##   .y.   group1 group2    n1    n2 statistic       p   p.adj p.adj.signif
## * <chr> <chr>  <chr>  <int> <int>     <dbl>   <dbl>   <dbl> <chr>       
## 1 slope HF     LF        24    24       157 0.00633 0.00633 **          
## # ℹ 1 more variable: p.adj.format <chr>
## Calculate positions statistics on plot
stat.in2 <- stat.in2 %>% add_xy_position(x = OUTER.VAR, dodge = 0.8)
stat.out2 <- stat.out2 %>% add_xy_position(x = OUTER.VAR, dodge = 0.9)
stat.out2$y.position <- max(stat.in2$y.position)*1.1

# Sort dat.clean
rl_rats <- rl_rats[order(rl_rats$feedtreat),]

# Visualise growth rates per group with statistics
p <- rl_rats %>%
  ggboxplot(x = "feed",
            y = "slope",
            color = "feedtreat",
            palette = params$COL1)
p2 <- p + stat_pvalue_manual(stat.in2, label = "p.adj.format",tip.length = 0, hide.ns = FALSE) +
  stat_pvalue_manual(stat.out2, label = "p.adj.format",tip.length = 0, hide.ns = FALSE) +
  scale_y_continuous(expand = expansion(mult = c(0,0.1)))
p2
## Warning: No shared levels found between `names(values)` of the manual scale and the
## data's fill values.

# Output plot
ggsave(filename = paste0("plots/animal_data/weight/bodyweight_slope_combined_plot.png"), p2, device = "png", dpi = 300, units = "mm", width = 100, height = 100)
## Warning: No shared levels found between `names(values)` of the manual scale and the
## data's fill values.
ggsave(filename = paste0("plots/animal_data/weight/bodyweight_slope_combined_plot.pdf"), p2, device = "pdf", dpi = 300, units = "mm", width = 100, height = 100)
## Warning: No shared levels found between `names(values)` of the manual scale and the
## data's fill values.
# Statistics for facet by day
stat.in1 <- rl_rats %>%
  group_by(.data[[OUTER.VAR]],.data[[FACETVAR]]) %>%
  wilcox_test(as.formula(paste("slope ~",INNER.VAR, sep = " "))) %>%
  adjust_pvalue(method = "BH") %>%
  add_significance("p.adj") %>% 
  p_format("p.adj", accuracy = 0.0001, trailing.zero = TRUE, new.col = TRUE)
stat.in1
## # A tibble: 4 × 12
##   feed  day   .y.   group1 group2    n1    n2 statistic      p p.adj
## * <chr> <chr> <chr> <chr>  <chr>  <int> <int>     <dbl>  <dbl> <dbl>
## 1 HF    d21   slope CTRL   PFOS       6     6         9 0.18   0.36 
## 2 HF    d8    slope CTRL   PFOS       6     6        24 0.394  0.394
## 3 LF    d21   slope CTRL   PFOS       6     6        29 0.0931 0.36 
## 4 LF    d8    slope CTRL   PFOS       6     6        12 0.394  0.394
## # ℹ 2 more variables: p.adj.signif <chr>, p.adj.format <chr>
stat.out1 <- rl_rats %>%
  group_by(.data[[FACETVAR]]) %>%
  wilcox_test(as.formula(paste("slope ~",OUTER.VAR, sep = " "))) %>%
  adjust_pvalue(method = "BH") %>%
  add_significance("p.adj") %>%
  p_format("p.adj", accuracy = 0.0001, trailing.zero = TRUE, new.col = TRUE)
stat.out1
## # A tibble: 2 × 11
##   day   .y.   group1 group2    n1    n2 statistic        p    p.adj p.adj.signif
## * <chr> <chr> <chr>  <chr>  <int> <int>     <dbl>    <dbl>    <dbl> <chr>       
## 1 d21   slope HF     LF        12    12        14 0.000371 0.000742 ***         
## 2 d8    slope HF     LF        12    12        55 0.347    0.347    ns          
## # ℹ 1 more variable: p.adj.format <chr>
## Calculate positions statistics on plot
stat.in1 <- stat.in1 %>% add_xy_position(x = OUTER.VAR, dodge = 0.8)
stat.out1 <- stat.out1 %>% add_xy_position(x = OUTER.VAR, dodge = 0.9)
stat.out1$y.position <- max(stat.in1$y.position)*1.1

# Sort dat.clean
rl_rats <- rl_rats[order(rl_rats$feedtreat),]

# Visualise growth rates per group with statistics
p <- rl_rats %>%
  ggboxplot(x = "feed",
            y = "slope",
            color = "feedtreat",
            palette = params$COL1,
            facet.by = "day")
p1 <- p + stat_pvalue_manual(stat.in1, label = "p.adj.format",tip.length = 0, hide.ns = FALSE) +
  stat_pvalue_manual(stat.out1, label = "p.adj.format",tip.length = 0, hide.ns = FALSE) +
  scale_y_continuous(expand = expansion(mult = c(0,0.1)))
p1
## Warning: No shared levels found between `names(values)` of the manual scale and the
## data's fill values.

# Output plot
ggsave(filename = paste0("plots/animal_data/weight/bodyweight_slope_",FACETVAR,"_plot.png"), p2, device = "png", dpi = 300, units = "mm", width = 100, height = 100)
## Warning: No shared levels found between `names(values)` of the manual scale and the
## data's fill values.
ggsave(filename = paste0("plots/animal_data/weight/bodyweight_slope_",FACETVAR,"_plot.pdf"), p2, device = "pdf", dpi = 300, units = "mm", width = 100, height = 100)
## Warning: No shared levels found between `names(values)` of the manual scale and the
## data's fill values.

5.4.3 Create figure

# Mean and SD for growth rates for plotting
rl_rats_msd <- rl_rats %>% 
  group_by(feedtreat) %>% get_summary_stats(!!sym("slope"), type = "mean_sd")

# Slope texts for plot
slope_text <- data.frame(label = c(paste0("Rate = ",round(rl_rats_msd$mean[1],2),"(",round(rl_rats_msd$sd[1],2),") g/day"),
                                   paste0("Rate = ",round(rl_rats_msd$mean[2],2),"(",round(rl_rats_msd$sd[2],2),") g/day"),
                                   paste0("Rate = ",round(rl_rats_msd$mean[3],2),"(",round(rl_rats_msd$sd[3],2),") g/day"),
                                   paste0("Rate = ",round(rl_rats_msd$mean[4],2),"(",round(rl_rats_msd$sd[4],2),") g/day")),
                         feedtreat = names(table(dat.bw$feedtreat)),
                         x = c(rep(15, times = 4)), y = c(rep(1.15, times = 4)))

# Summary samples in groups
dat.bwn %>% group_by(across(all_of(PREDICTOR))) %>% get_summary_stats(!!sym(OUTCOME), type = "mean_sd")
## # A tibble: 48 × 7
##    feed  treatment bw_time_n variable     n  mean    sd
##    <chr> <chr>     <chr>     <fct>    <dbl> <dbl> <dbl>
##  1 HF    CTRL      bw_0_n    wt_n        12  1    0    
##  2 LF    CTRL      bw_0_n    wt_n        12  1    0    
##  3 HF    PFOS      bw_0_n    wt_n        12  1    0    
##  4 LF    PFOS      bw_0_n    wt_n        12  1    0    
##  5 HF    CTRL      bw_12_n   wt_n         6  1.30 0.051
##  6 LF    CTRL      bw_12_n   wt_n         6  1.35 0.043
##  7 HF    PFOS      bw_12_n   wt_n         6  1.31 0.029
##  8 LF    PFOS      bw_12_n   wt_n         6  1.32 0.02 
##  9 HF    CTRL      bw_16_n   wt_n         6  1.38 0.045
## 10 LF    CTRL      bw_16_n   wt_n         6  1.48 0.047
## # ℹ 38 more rows
# Setup rectangles
rec.df <- data.frame(x0 = -0.5, x1 = 0.5, x2 = 7.5, x3 = 21.5, y1 = 1.58, y2 = 1.6809, c0 = "a", c1 = "b", c2 = "c", t0 = "T0", t1 = "Dosing period", t2 = "Wash-out period")

# Generate plot
p <- ggplot(data = dat.bwn, aes(x = day, y = wt_n, fill = feedtreat)) +
  geom_rect(data = rec.df, mapping = aes(xmin = x0, xmax = x1, ymin = y1, ymax = y2, fill = c0), fill = "#f0f0f0", inherit.aes = FALSE) + 
  geom_text(data = rec.df, aes(x = x0+(x1-x0)/2, y = y1+(y2-y1)/2, label = t0), size = 2.5, inherit.aes = FALSE) +
  geom_rect(data = rec.df, mapping = aes(xmin = x1, xmax = x2, ymin = y1, ymax = y2, fill = c1), fill = "#ffffa0", inherit.aes = FALSE) +
  geom_text(data = rec.df, aes(x = x1+(x2-x1)/2, y = y1+(y2-y1)/2, label = t1), size = 2.5, inherit.aes = FALSE) +
  geom_rect(data = rec.df, mapping = aes(xmin = x2, xmax = x3, ymin = y1, ymax = y2, fill = c2), fill = "#ffd0d0", inherit.aes = FALSE) +
  geom_text(data = rec.df, aes(x = x2+(x3-x2)/2, y = y1+(y2-y1)/2, label = t2), size = 2.5, inherit.aes = FALSE) +
  geom_vline(data = NULL, xintercept = 8, linetype = "dashed", color = "#909090") +
  geom_vline(data = NULL, xintercept = 21, linetype = "dashed", color = "#909090") +
  geom_line(aes(group = rat_name, color = feedtreat), stat = "identity", alpha = 0.6) +
  geom_smooth(aes(group = feedtreat, color = feedtreat), color = "#090909", method = "lm", se = FALSE, size = 0.5, fullrange = TRUE, linetype = "dashed") +
  geom_boxplot(aes(group = day), outlier.shape = NA) + 
  geom_point(size = 0.5) +
  scale_fill_manual(values = params$COL1, labels = c("HF-CTRL","HF-PFOS","LF-CTRL","LF-PFOS"), name = "Groups") +
  scale_color_manual(values = params$COL1) + 
  scale_y_continuous(name = "Body weight ratio", limits = c(0.9,1.7), breaks = seq(1,1.7,0.2)) +
  scale_x_continuous(name = "Day", breaks = c(0,1,2,3,4,5,6,7,8,12,16,21)) +
  theme_pubr() +
  guides(color = "none", alpha = "none") +
  facet_wrap(.~feedtreat, labeller = labeller(feedtreat = c("LF_CTRL" = "LF-CTRL","LF_PFOS" = "LF-PFOS","HF_CTRL" = "HF-CTRL","HF_PFOS" = "HF-PFOS"))) +
  geom_text(data = slope_text, aes(x = x, y = y, label = label), position = position_dodge(width = 0.9), size = 3.5)
## Warning: Using `size` aesthetic for lines was deprecated in ggplot2 3.4.0.
## ℹ Please use `linewidth` instead.
## This warning is displayed once every 8 hours.
## Call `lifecycle::last_lifecycle_warnings()` to see where this warning was
## generated.
p
## `geom_smooth()` using formula = 'y ~ x'

# Save plot
suppressMessages(ggsave(filename = "plots/animal_data/weight/bodyweight_norm_over_time.png", plot = p, device = "png", dpi = 300, units = "mm", height = 150, width = 260))
suppressMessages(ggsave(filename = "plots/animal_data/weight/bodyweight_norm_over_time.pdf", plot = p, device = "pdf", dpi = 300, units = "mm", height = 150, width = 260))

# clear the environment and release memory
rm(list = ls(all.names = TRUE)) #will clear all objects includes hidden objects.
invisible(gc()) #free up memory and report the memory usage.

5.5 Cecum weight

This section will prepare to perform the data analysis for cecum weight-to-body weight ratio data presented as normalised data for easy interpretation. Data is normalised after no fiber group control on respective days,

5.5.1 Prepare data

This section sets the variables to be used and prepares the data if necessary.

# load data 
load("R_objects/animal_data.Rdata")
params <- readRDS("R_objects/animal_params.RDS")
dat.clean <- subset(dat, !is.na(cecum_norm))

# Set names of variables
PREDICTOR <- c("treatment","feed","dissection")
OUTCOME <- "cecum_wtbw"
SUBJECT <- "rat_name"

# Create formula
PREDICTOR.F <- ifelse(length(PREDICTOR) > 1, paste(PREDICTOR, collapse = "*"), PREDICTOR)
FORMULA <- as.formula(paste(OUTCOME,PREDICTOR.F, sep = " ~ "))

# Summary samples in groups
dat.clean %>% group_by(across(all_of(PREDICTOR))) %>% get_summary_stats(!!sym(OUTCOME), type = "mean_sd")
## # A tibble: 8 × 7
##   dissection treatment feed  variable       n  mean    sd
##   <chr>      <chr>     <chr> <fct>      <dbl> <dbl> <dbl>
## 1 d08        CTRL      HF    cecum_wtbw     6 19.6   4.34
## 2 d21        CTRL      HF    cecum_wtbw     6 17.5   3.31
## 3 d08        CTRL      LF    cecum_wtbw     6  6.82  2.28
## 4 d21        CTRL      LF    cecum_wtbw     6  6.85  1.30
## 5 d08        PFOS      HF    cecum_wtbw     6 19.4   3.73
## 6 d21        PFOS      HF    cecum_wtbw     6 18.9   2.39
## 7 d08        PFOS      LF    cecum_wtbw     6  6.96  1.09
## 8 d21        PFOS      LF    cecum_wtbw     6  7.02  1.29
# SUmmary of original weight in grams
dat.clean %>% group_by(across(all_of(PREDICTOR))) %>% get_summary_stats(cecum_wt, type = "mean_sd")
## # A tibble: 8 × 7
##   dissection treatment feed  variable     n  mean    sd
##   <chr>      <chr>     <chr> <fct>    <dbl> <dbl> <dbl>
## 1 d08        CTRL      HF    cecum_wt     6  6.90 1.61 
## 2 d21        CTRL      HF    cecum_wt     6  6.90 1.4  
## 3 d08        CTRL      LF    cecum_wt     6  2.28 0.709
## 4 d21        CTRL      LF    cecum_wt     6  3.1  0.476
## 5 d08        PFOS      HF    cecum_wt     6  6.62 1.27 
## 6 d21        PFOS      HF    cecum_wt     6  7.84 1.37 
## 7 d08        PFOS      LF    cecum_wt     6  2.47 0.375
## 8 d21        PFOS      LF    cecum_wt     6  2.93 0.457
dat.clean %>% group_by(across(all_of(PREDICTOR))) %>% get_summary_stats(cecum_wt, type = "full")
## # A tibble: 8 × 16
##   dissection treatment feed  variable     n   min   max median    q1    q3   iqr
##   <chr>      <chr>     <chr> <fct>    <dbl> <dbl> <dbl>  <dbl> <dbl> <dbl> <dbl>
## 1 d08        CTRL      HF    cecum_wt     6  5.15  9.51   6.55  5.79  7.69 1.89 
## 2 d21        CTRL      HF    cecum_wt     6  5.57  9.03   6.47  5.83  7.80 1.97 
## 3 d08        CTRL      LF    cecum_wt     6  1.16  3.12   2.45  1.91  2.69 0.775
## 4 d21        CTRL      LF    cecum_wt     6  2.42  3.50   3.32  2.74  3.45 0.707
## 5 d08        PFOS      HF    cecum_wt     6  4.72  8.56   6.60  6.18  7.06 0.884
## 6 d21        PFOS      HF    cecum_wt     6  6.41 10.1    7.4   6.98  8.54 1.56 
## 7 d08        PFOS      LF    cecum_wt     6  1.99  3.11   2.43  2.30  2.56 0.268
## 8 d21        PFOS      LF    cecum_wt     6  2.57  3.59   2.72  2.58  3.28 0.69 
## # ℹ 5 more variables: mad <dbl>, mean <dbl>, sd <dbl>, se <dbl>, ci <dbl>
# Create plot
bxp <- dat.clean %>%
  ggboxplot(x = if_else(length(PREDICTOR) > 1, PREDICTOR[2],PREDICTOR[1]),
            y = OUTCOME,
            color = PREDICTOR[1],
            facet.by = if(length(PREDICTOR) == 3) PREDICTOR[3],
            palette = "jco")
bxp

# Test for outliers
dat.clean %>% 
  group_by(across(all_of(PREDICTOR))) %>% 
  identify_outliers(!!sym(OUTCOME))
##  [1] dissection        treatment         feed              rat_org          
##  [5] rat_name          cage              feedtreat         feedtreatday     
##  [9] bw_minus18        bw_0              bw_1              bw_2             
## [13] bw_3              bw_4              bw_5              bw_6             
## [17] bw_7              bw_8              bw_12             bw_16            
## [21] bw_21             bw_gain08         bw_gain021        bw_gain821       
## [25] bloodvol_8        bloodvol_16       bloodvol_21       cecum_wt         
## [29] cecum_wtbw        cecum_norm        liver_wt          liver_wtbw       
## [33] liver_norm        brain_wt_estimate transit_0         transit_7        
## [37] transit_20        dose_total_ml     pfos_total_mg     pfos_liver_ugg   
## [41] pfos_liver_mg     pfos_liver8_pct   pfos_brain_ugg    pfos_brain_uggbw 
## [45] pfos_brain_ug     pfos_brain_pct    pfos_serum8_ugml  pfos_serum8_mg   
## [49] pfos_serum8_pct   pfos_serum16_ugml pfos_serum16_mg   pfos_serum16_pct 
## [53] pfos_serum21_ugml pfos_serum21_mg   pfos_serum21_pct  pfos_feces8_ugg  
## [57] pfos_feces12_ugg  pfos_feces16_ugg  pfos_feces21_ugg  pfos_cecum_ugg   
## [61] pfos_urine8_ugml  pfos_urine16_ugml pfos_urine21_ugml pH_je_up         
## [65] pH_je_down        pH_ileum          pH_cecum          acetic           
## [69] formic            propanoic         m2_propanoic      butanoic         
## [73] m3_butanoic       pentanoic         m4_pentanoic      hexanoic         
## [77] heptanoic         is.outlier        is.extreme       
## <0 rækker> (eller 0-længde row.names)
# Build the linear model
model  <- lm(FORMULA, data = dat.clean)
# Create a QQ plot of residuals
ggqqplot(residuals(model))

# Compute Shapiro-Wilk test of normality
shapiro_test(residuals(model))
## # A tibble: 1 × 3
##   variable         statistic p.value
##   <chr>                <dbl>   <dbl>
## 1 residuals(model)     0.983   0.719
plot(model, 1)

dat.clean %>% levene_test(FORMULA)
## # A tibble: 1 × 4
##     df1   df2 statistic     p
##   <int> <int>     <dbl> <dbl>
## 1     7    40      1.73 0.130
# Save result
EQUAL.VAR <- dat.clean %>% levene_test(FORMULA) %>% pull(p) > 0.05

This all shows that cecum weight data has three none-critical outliers, is normally distribution and has equal variance. Therefore we use a one-way ANOVA test with Tukey’s honest significance test to test data for all factors of interest, while using unpaired t-test for pairwise comparisons.

5.5.2 ANOVA One-Way test

Perform test

If we had equality of variance we can now run a one-way ANOVA tests anova_test() (if we have equal variance) or a welch_anova_test() (if variance vary).

if(EQUAL.VAR) {
  res.aov <- dat.clean %>% anova_test(FORMULA)
  res.aov
} else {
  res.aov <- dat.clean %>% welch_anova_test(FORMULA)
  res.aov
}
## ANOVA Table (type II tests)
## 
##                      Effect DFn DFd       F        p p<.05   ges
## 1                 treatment   1  40   0.258 6.14e-01       0.006
## 2                      feed   1  40 232.040 3.02e-18     * 0.853
## 3                dissection   1  40   0.620 4.36e-01       0.015
## 4            treatment:feed   1  40   0.100 7.54e-01       0.002
## 5      treatment:dissection   1  40   0.256 6.15e-01       0.006
## 6           feed:dissection   1  40   0.713 4.03e-01       0.018
## 7 treatment:feed:dissection   1  40   0.235 6.30e-01       0.006

ANOVA shows that feed has the only significant effect on the overall data.

Perform posthoc test

A significant one-way ANOVA is generally followed up by Tukey post-hoc tests to perform multiple pairwise comparisons between groups. When running relaxed Welch one-way test, the Games-Howell post hoc test or pairwise t-tests (with no assumption of equal variances) can be used to compare all possible combinations of group differences.

if(EQUAL.VAR) {
  pwc <- dat.clean %>% tukey_hsd(FORMULA)
  pwc
} else {
  pwc <- dat.clean %>% games_howell_test(FORMULA)
  pwc
}
## # A tibble: 49 × 9
##    term            group1 group2 null.value estimate conf.low conf.high    p.adj
##  * <chr>           <chr>  <chr>       <dbl>    <dbl>    <dbl>     <dbl>    <dbl>
##  1 treatment       CTRL   PFOS            0  3.99e-1    -1.19     1.98  6.14e- 1
##  2 feed            HF     LF              0 -1.20e+1   -13.5    -10.4   4.64e-13
##  3 dissection      d08    d21             0 -6.18e-1    -2.20     0.969 4.36e- 1
##  4 treatment:feed  CTRL:… PFOS:…          0  6.47e-1    -2.33     3.62  9.37e- 1
##  5 treatment:feed  CTRL:… CTRL:…          0 -1.17e+1   -14.7     -8.73  2.91e-12
##  6 treatment:feed  CTRL:… PFOS:…          0 -1.16e+1   -14.5     -8.58  4.04e-12
##  7 treatment:feed  PFOS:… CTRL:…          0 -1.24e+1   -15.3     -9.38  9.64e-13
##  8 treatment:feed  PFOS:… PFOS:…          0 -1.22e+1   -15.2     -9.23  1.18e-12
##  9 treatment:feed  CTRL:… PFOS:…          0  1.51e-1    -2.82     3.13  9.99e- 1
## 10 treatment:diss… CTRL:… PFOS:…          0  1.15e-3    -2.97     2.98  1   e+ 0
## # ℹ 39 more rows
## # ℹ 1 more variable: p.adj.signif <chr>

5.5.3 Nested analysis & Figure

Testing two variables are used when there is a nested aspect in the analysis, for example difference in treatment at each timepoint, that would have the timepoint as the outer variable and treatment as the inner variable

# Set variables for inner and outer analysis, and variable for facet
INNER.VAR <- "treatment"
OUTER.VAR <- "feed"
FACETVAR <- "dissection"

# Remove samples with incomplete metadata
dat.clean <- dat.clean[!is.na(dat.clean[,INNER.VAR]) & !is.na(dat.clean[,OUTER.VAR]),]

# Set variables as factors
dat.clean[,OUTER.VAR] <- as.factor(dat.clean[,OUTER.VAR])
dat.clean[,INNER.VAR] <- as.factor(dat.clean[,INNER.VAR])
dat.clean[,FACETVAR] <- as.factor(dat.clean[,FACETVAR])

# Cecum_norm
fit <- aov(as.formula(paste(OUTCOME,"~", OUTER.VAR,"*",INNER.VAR, sep = " ")), data = dat.clean)
anova(fit)
## Analysis of Variance Table
## 
## Response: cecum_wtbw
##                Df  Sum Sq Mean Sq  F value Pr(>F)    
## feed            1 1715.39 1715.39 244.1094 <2e-16 ***
## treatment       1    1.91    1.91   0.2713 0.6051    
## feed:treatment  1    0.74    0.74   0.1050 0.7474    
## Residuals      44  309.19    7.03                    
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
TukeyHSD(fit)
##   Tukey multiple comparisons of means
##     95% family-wise confidence level
## 
## Fit: aov(formula = as.formula(paste(OUTCOME, "~", OUTER.VAR, "*", INNER.VAR, sep = " ")), data = dat.clean)
## 
## $feed
##            diff       lwr       upr p adj
## LF-HF -11.95613 -13.49838 -10.41389     0
## 
## $treatment
##                diff       lwr      upr     p adj
## PFOS-CTRL 0.3986117 -1.143631 1.940854 0.6050505
## 
## $`feed:treatment`
##                        diff        lwr       upr     p adj
## LF:CTRL-HF:CTRL -11.7081226 -14.597642 -8.818603 0.0000000
## HF:PFOS-HF:CTRL   0.6466221  -2.242897  3.536142 0.9322867
## LF:PFOS-HF:CTRL -11.5575214 -14.447041 -8.668002 0.0000000
## HF:PFOS-LF:CTRL  12.3547447   9.465225 15.244264 0.0000000
## LF:PFOS-LF:CTRL   0.1506012  -2.738918  3.040121 0.9990233
## LF:PFOS-HF:PFOS -12.2041435 -15.093663 -9.314624 0.0000000
# Statistics costumed for facet plotting
## Pairwise comparison for inner variable
stat.in <- dat.clean %>%
  group_by(.data[[OUTER.VAR]], .data[[FACETVAR]]) %>%
  t_test(as.formula(paste(OUTCOME,"~",INNER.VAR, sep = " ")), 
         paired = FALSE, var.equal = EQUAL.VAR, 
         detailed = TRUE, alternative = "two.sided") %>%
  add_significance() %>%
  p_format("p", accuracy = 0.001, trailing.zero = TRUE, new.col = TRUE)
stat.in
## # A tibble: 4 × 19
##   dissection feed  estimate estimate1 estimate2 .y.    group1 group2    n1    n2
## * <fct>      <fct>    <dbl>     <dbl>     <dbl> <chr>  <chr>  <chr>  <int> <int>
## 1 d08        HF       0.132     19.6      19.4  cecum… CTRL   PFOS       6     6
## 2 d21        HF      -1.42      17.5      18.9  cecum… CTRL   PFOS       6     6
## 3 d08        LF      -0.134      6.82      6.96 cecum… CTRL   PFOS       6     6
## 4 d21        LF      -0.167      6.85      7.02 cecum… CTRL   PFOS       6     6
## # ℹ 9 more variables: statistic <dbl>, p <dbl>, df <dbl>, conf.low <dbl>,
## #   conf.high <dbl>, method <chr>, alternative <chr>, p.signif <chr>,
## #   p.format <chr>
## Pairwise comparison for outer variable
stat.out <- dat.clean %>%
  group_by(.data[[FACETVAR]]) %>%
  t_test(as.formula(paste(OUTCOME,"~", OUTER.VAR, sep = " ")), 
         paired = FALSE, var.equal = EQUAL.VAR, 
         detailed = TRUE, alternative = "two.sided") %>%
  add_significance() %>%
  p_format("p", accuracy = 0.001, trailing.zero = TRUE, new.col = TRUE)
stat.out
## # A tibble: 2 × 18
##   dissection estimate estimate1 estimate2 .y.        group1 group2    n1    n2
## * <fct>         <dbl>     <dbl>     <dbl> <chr>      <chr>  <chr>  <int> <int>
## 1 d08            12.6      19.5      6.89 cecum_wtbw HF     LF        12    12
## 2 d21            11.3      18.2      6.94 cecum_wtbw HF     LF        12    12
## # ℹ 9 more variables: statistic <dbl>, p <dbl>, df <dbl>, conf.low <dbl>,
## #   conf.high <dbl>, method <chr>, alternative <chr>, p.signif <chr>,
## #   p.format <chr>
## Calculate positions statistics on plot
stat.in <- stat.in %>% add_xy_position(x = OUTER.VAR, dodge = 0.8)
stat.out <- stat.out %>% add_xy_position(x = OUTER.VAR)
stat.out$y.position <- max(stat.in$y.position)*1.1

# Create plot
p <- ggboxplot(dat.clean, x = OUTER.VAR, y = "cecum_norm",
               color = "feedtreat",
               fill = "feedtreat",
               add = "jitter",
               add.params = list(size = 1),
               panel.labs = list("dissection" = c("Day 8","Day 21")),
               facet.by = "dissection") +
  theme_pubr(legend = "top") +
  scale_fill_manual(breaks = dat.clean$treatment, values = params$COL1) +
  scale_color_manual(breaks = dat.clean$treatment, values = c("HF_CTRL" = "black","HF_PFOS" = "black","LF_CTRL" = "black","LF_PFOS" = "black")) +
  scale_x_discrete(name = "Feed") +
  guides(color = FALSE) +
  theme(axis.title.x = element_blank())
## Warning: The `<scale>` argument of `guides()` cannot be `FALSE`. Use "none" instead as
## of ggplot2 3.3.4.
## This warning is displayed once every 8 hours.
## Call `lifecycle::last_lifecycle_warnings()` to see where this warning was
## generated.
p

# Add p-values for inner and outer analysis to plot
p.stat <- p + 
  stat_pvalue_manual(stat.in, label = "p.signif", tip.length = 0, hide.ns = TRUE, color = "red") + 
  stat_pvalue_manual(stat.out, label = "p.signif", tip.length = 0, hide.ns = TRUE, y.position = c(1.45,1.45)) +
  scale_y_continuous(name ="Percentage from mean HF-CTRL", limits = c(0.15,1.5), breaks = seq(0,1.5,0.25), labels = function(x) paste0(x*100-100, "%"))
p.stat

### Create output plot
suppressMessages(ggsave(filename = paste0("plots/animal_data/weight/cecum_weight_nested_",OUTER.VAR,"_",INNER.VAR,".png"), plot = p.stat, device = "png", units = "mm", width = 130, height = 80, dpi = 300))
suppressMessages(ggsave(filename = paste0("plots/animal_data/weight/cecum_weight_nested_",OUTER.VAR,"_",INNER.VAR,".pdf"), plot = p.stat, device = "pdf", units = "mm", width = 130, height = 80, dpi = 300))

# clear the environment and release memory
rm(list = ls(all.names = TRUE)[ls(all.names = TRUE) != "params"])
invisible(gc())

5.6 Liver weight

This section will prepare to perform the data analysis for liver weight-to-body weight data, presented as normalised data for easy interpretation.

5.6.1 Prepare data

This section sets the variables to be used and prepares the data if necessary.

# load data 
load("R_objects/animal_data.Rdata")
params <- readRDS("R_objects/animal_params.RDS")
dat.clean <- subset(dat, !is.na(cecum_norm))

# Set names of variables
PREDICTOR <- c("treatment","feed","dissection")
OUTCOME <- "liver_wtbw"
SUBJECT <- "rat_name"

# Create formula
PREDICTOR.F <- ifelse(length(PREDICTOR) > 1, paste(PREDICTOR, collapse = "*"), PREDICTOR)
FORMULA <- as.formula(paste(OUTCOME,PREDICTOR.F, sep = " ~ "))

# Summary samples in groups
dat.clean %>% group_by(across(all_of(PREDICTOR))) %>% get_summary_stats(!!sym(OUTCOME), type = "mean_sd")
## # A tibble: 8 × 7
##   dissection treatment feed  variable       n  mean    sd
##   <chr>      <chr>     <chr> <fct>      <dbl> <dbl> <dbl>
## 1 d08        CTRL      HF    liver_wtbw     6  46.9  2.73
## 2 d21        CTRL      HF    liver_wtbw     6  39.3  3.14
## 3 d08        CTRL      LF    liver_wtbw     6  46.3  3.37
## 4 d21        CTRL      LF    liver_wtbw     6  42.2  4.44
## 5 d08        PFOS      HF    liver_wtbw     6  49.1  3.90
## 6 d21        PFOS      HF    liver_wtbw     6  43.4  3.92
## 7 d08        PFOS      LF    liver_wtbw     6  51.6  3.35
## 8 d21        PFOS      LF    liver_wtbw     6  42.2  4.34
# SUmmary of original weight in grams
dat.clean %>% group_by(across(all_of(PREDICTOR))) %>% get_summary_stats(liver_wt, type = "mean_sd")
## # A tibble: 8 × 7
##   dissection treatment feed  variable     n  mean    sd
##   <chr>      <chr>     <chr> <fct>    <dbl> <dbl> <dbl>
## 1 d08        CTRL      HF    liver_wt     6  16.6  2.42
## 2 d21        CTRL      HF    liver_wt     6  15.5  1.68
## 3 d08        CTRL      LF    liver_wt     6  15.7  2.40
## 4 d21        CTRL      LF    liver_wt     6  19.2  2.37
## 5 d08        PFOS      HF    liver_wt     6  16.8  1.99
## 6 d21        PFOS      HF    liver_wt     6  17.9  1.58
## 7 d08        PFOS      LF    liver_wt     6  18.6  3.38
## 8 d21        PFOS      LF    liver_wt     6  17.7  1.88
dat.clean %>% group_by(across(all_of(PREDICTOR))) %>% get_summary_stats(liver_wt, type = "full")
## # A tibble: 8 × 16
##   dissection treatment feed  variable     n   min   max median    q1    q3   iqr
##   <chr>      <chr>     <chr> <fct>    <dbl> <dbl> <dbl>  <dbl> <dbl> <dbl> <dbl>
## 1 d08        CTRL      HF    liver_wt     6  14.3  19.9   15.8  14.9  18.5  3.61
## 2 d21        CTRL      HF    liver_wt     6  13.4  17.3   16.0  14.0  16.6  2.61
## 3 d08        CTRL      LF    liver_wt     6  13.0  18.5   15.6  13.6  17.7  4.11
## 4 d21        CTRL      LF    liver_wt     6  16.9  23.4   18.4  18.0  20.0  2.01
## 5 d08        PFOS      HF    liver_wt     6  13.5  18.9   16.7  16.2  18.3  2.07
## 6 d21        PFOS      HF    liver_wt     6  15.6  20.1   18.3  17.0  18.4  1.38
## 7 d08        PFOS      LF    liver_wt     6  15.1  24.2   17.3  16.6  20.2  3.60
## 8 d21        PFOS      LF    liver_wt     6  15.4  19.8   17.6  16.4  19.4  3.07
## # ℹ 5 more variables: mad <dbl>, mean <dbl>, sd <dbl>, se <dbl>, ci <dbl>
# Create plot
bxp <- dat.clean %>%
  ggboxplot(x = if_else(length(PREDICTOR) > 1, PREDICTOR[2],PREDICTOR[1]),
            y = OUTCOME,
            color = PREDICTOR[1],
            facet.by = if(length(PREDICTOR) == 3) PREDICTOR[3],
            palette = "jco")
bxp

# Test for outliers
dat.clean %>% 
  group_by(across(all_of(PREDICTOR))) %>% 
  identify_outliers(!!sym(OUTCOME))
## # A tibble: 2 × 79
##   dissection treatment feed  rat_org rat_name  cage feedtreat feedtreatday
##   <chr>      <chr>     <chr> <chr>   <chr>    <int> <chr>     <chr>       
## 1 d08        PFOS      HF    R39     R39         20 HF_PFOS   HF_PFOS_d08 
## 2 d08        PFOS      HF    R43     R41         22 HF_PFOS   HF_PFOS_d08 
## # ℹ 71 more variables: bw_minus18 <dbl>, bw_0 <dbl>, bw_1 <int>, bw_2 <int>,
## #   bw_3 <int>, bw_4 <int>, bw_5 <int>, bw_6 <int>, bw_7 <int>, bw_8 <int>,
## #   bw_12 <dbl>, bw_16 <dbl>, bw_21 <dbl>, bw_gain08 <dbl>, bw_gain021 <dbl>,
## #   bw_gain821 <dbl>, bloodvol_8 <dbl>, bloodvol_16 <dbl>, bloodvol_21 <dbl>,
## #   cecum_wt <dbl>, cecum_wtbw <dbl>, cecum_norm <dbl>, liver_wt <dbl>,
## #   liver_wtbw <dbl>, liver_norm <dbl>, brain_wt_estimate <dbl>,
## #   transit_0 <int>, transit_7 <int>, transit_20 <int>, dose_total_ml <dbl>, …
# Build the linear model
model  <- lm(FORMULA, data = dat.clean)
# Create a QQ plot of residuals
ggqqplot(residuals(model))

# Compute Shapiro-Wilk test of normality
shapiro_test(residuals(model))
## # A tibble: 1 × 3
##   variable         statistic p.value
##   <chr>                <dbl>   <dbl>
## 1 residuals(model)     0.980   0.559
plot(model, 1)

dat.clean %>% levene_test(FORMULA)
## # A tibble: 1 × 4
##     df1   df2 statistic     p
##   <int> <int>     <dbl> <dbl>
## 1     7    40     0.225 0.977
# Save result
EQUAL.VAR <- dat.clean %>% levene_test(FORMULA) %>% pull(p) > 0.05

This all shows that normalised cecum weight data has two outliers, is normally distribution and has equal variance. Therefore we use a one-way ANOVA test with Tukey’s honest significance test to test data for all factors of interest, while using unpaired t-test for pairwise comparisons.

5.6.2 ANOVA One-Way test

Perform test

If we had equality of variance we can now run a one-way ANOVA tests anova_test() (if we have equal variance) or a welch_anova_test() (if variance vary).

if(EQUAL.VAR) {
  res.aov <- dat.clean %>% anova_test(FORMULA)
  res.aov
} else {
  res.aov <- dat.clean %>% welch_anova_test(FORMULA)
  res.aov
}
## ANOVA Table (type II tests)
## 
##                      Effect DFn DFd      F        p p<.05      ges
## 1                 treatment   1  40  7.564 9.00e-03     * 1.59e-01
## 2                      feed   1  40  0.762 3.88e-01       1.90e-02
## 3                dissection   1  40 39.215 2.02e-07     * 4.95e-01
## 4            treatment:feed   1  40  0.059 8.09e-01       1.00e-03
## 5      treatment:dissection   1  40  0.627 4.33e-01       1.50e-02
## 6           feed:dissection   1  40  0.001 9.70e-01       3.47e-05
## 7 treatment:feed:dissection   1  40  2.967 9.30e-02       6.90e-02

ANOVA shows that treatment and day of dissection for the samples have significant effects on the overall data.

Perform posthoc test

A significant one-way ANOVA is generally followed up by Tukey post-hoc tests to perform multiple pairwise comparisons between groups. When running relaxed Welch one-way test, the Games-Howell post hoc test or pairwise t-tests (with no assumption of equal variances) can be used to compare all possible combinations of group differences.

if(EQUAL.VAR) {
  pwc <- dat.clean %>% tukey_hsd(FORMULA)
  pwc
} else {
  pwc <- dat.clean %>% games_howell_test(FORMULA)
  pwc
}
## # A tibble: 49 × 9
##    term             group1 group2 null.value estimate conf.low conf.high   p.adj
##  * <chr>            <chr>  <chr>       <dbl>    <dbl>    <dbl>     <dbl>   <dbl>
##  1 treatment        CTRL   PFOS            0    2.93     0.777      5.08 8.9 e-3
##  2 feed             HF     LF              0    0.930   -1.22       3.08 3.88e-1
##  3 dissection       d08    d21             0   -6.67    -8.83      -4.52 2.02e-7
##  4 treatment:feed   CTRL:… PFOS:…          0    3.19    -0.850      7.23 1.65e-1
##  5 treatment:feed   CTRL:… CTRL:…          0    1.19    -2.85       5.23 8.59e-1
##  6 treatment:feed   CTRL:… PFOS:…          0    3.86    -0.178      7.90 6.55e-2
##  7 treatment:feed   PFOS:… CTRL:…          0   -2.00    -6.04       2.04 5.51e-1
##  8 treatment:feed   PFOS:… PFOS:…          0    0.671   -3.37       4.71 9.7 e-1
##  9 treatment:feed   CTRL:… PFOS:…          0    2.67    -1.37       6.71 3.01e-1
## 10 treatment:disse… CTRL:… PFOS:…          0    3.77    -0.265      7.81 7.45e-2
## # ℹ 39 more rows
## # ℹ 1 more variable: p.adj.signif <chr>

Post hoc analysis however show only significant differences between groupings to be between overall treatment, overall dissection days, as well as cross-day comparisons.

5.6.3 Nested analysis & Figure

Testing two variables are used when there is a nested aspect in the analysis, for example difference in treatment at each timepoint, that would have the timepoint as the outer variable and treatment as the inner variable

# Set variables for inner and outer analysis, and variable for facet
INNER.VAR <- "treatment"
OUTER.VAR <- "feed"
FACETVAR <- "dissection"

# Remove samples with incomplete metadata
dat.clean <- dat.clean[!is.na(dat.clean[,INNER.VAR]) & !is.na(dat.clean[,OUTER.VAR]),]

# Set variables as factors
dat.clean[,OUTER.VAR] <- as.factor(dat.clean[,OUTER.VAR])
dat.clean[,INNER.VAR] <- as.factor(dat.clean[,INNER.VAR])
dat.clean[,FACETVAR] <- as.factor(dat.clean[,FACETVAR])

# Cecum_norm
fit <- aov(as.formula(paste(OUTCOME,"~", OUTER.VAR,"*",INNER.VAR, sep = " ")), data = dat.clean)
anova(fit)
## Analysis of Variance Table
## 
## Response: liver_wtbw
##                Df  Sum Sq Mean Sq F value  Pr(>F)  
## feed            1   10.38  10.384  0.4050 0.52780  
## treatment       1  103.04 103.039  4.0190 0.05117 .
## feed:treatment  1    0.80   0.804  0.0314 0.86027  
## Residuals      44 1128.06  25.638                  
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
TukeyHSD(fit)
##   Tukey multiple comparisons of means
##     95% family-wise confidence level
## 
## Fit: aov(formula = as.formula(paste(OUTCOME, "~", OUTER.VAR, "*", INNER.VAR, sep = " ")), data = dat.clean)
## 
## $feed
##            diff       lwr      upr     p adj
## LF-HF 0.9302309 -2.015571 3.876032 0.5278019
## 
## $treatment
##               diff         lwr     upr     p adj
## PFOS-CTRL 2.930289 -0.01551291 5.87609 0.0511665
## 
## $`feed:treatment`
##                      diff       lwr      upr     p adj
## LF:CTRL-HF:CTRL 1.1890424 -4.330161 6.708246 0.9389701
## HF:PFOS-HF:CTRL 3.1891000 -2.330103 8.708303 0.4213654
## LF:PFOS-HF:CTRL 3.8605195 -1.658684 9.379723 0.2566549
## HF:PFOS-LF:CTRL 2.0000577 -3.519146 7.519261 0.7684275
## LF:PFOS-LF:CTRL 2.6714771 -2.847726 8.190680 0.5725927
## LF:PFOS-HF:PFOS 0.6714195 -4.847784 6.190623 0.9879985
# Statistics costumed for facet plotting
## Pairwise comparison for inner variable
stat.in <- dat.clean %>%
  group_by(.data[[OUTER.VAR]], .data[[FACETVAR]]) %>%
  t_test(as.formula(paste(OUTCOME,"~",INNER.VAR, sep = " ")), 
         paired = FALSE, var.equal = EQUAL.VAR, 
         detailed = TRUE, alternative = "two.sided") %>%
  add_significance() %>%
  p_format("p", accuracy = 0.001, trailing.zero = TRUE, new.col = TRUE)
stat.in
## # A tibble: 4 × 19
##   dissection feed  estimate estimate1 estimate2 .y.    group1 group2    n1    n2
## * <fct>      <fct>    <dbl>     <dbl>     <dbl> <chr>  <chr>  <chr>  <int> <int>
## 1 d08        HF    -2.20         46.9      49.1 liver… CTRL   PFOS       6     6
## 2 d21        HF    -4.18         39.3      43.4 liver… CTRL   PFOS       6     6
## 3 d08        LF    -5.35         46.3      51.6 liver… CTRL   PFOS       6     6
## 4 d21        LF     0.00726      42.2      42.2 liver… CTRL   PFOS       6     6
## # ℹ 9 more variables: statistic <dbl>, p <dbl>, df <dbl>, conf.low <dbl>,
## #   conf.high <dbl>, method <chr>, alternative <chr>, p.signif <chr>,
## #   p.format <chr>
## Pairwise comparison for outer variable
stat.out <- dat.clean %>%
  group_by(.data[[FACETVAR]]) %>%
  t_test(as.formula(paste(OUTCOME,"~", OUTER.VAR, sep = " ")), 
         paired = FALSE, var.equal = EQUAL.VAR, 
         detailed = TRUE, alternative = "two.sided") %>%
  add_significance() %>%
  p_format("p", accuracy = 0.001, trailing.zero = TRUE, new.col = TRUE)
stat.out
## # A tibble: 2 × 18
##   dissection estimate estimate1 estimate2 .y.        group1 group2    n1    n2
## * <fct>         <dbl>     <dbl>     <dbl> <chr>      <chr>  <chr>  <int> <int>
## 1 d08          -0.970      48.0      49.0 liver_wtbw HF     LF        12    12
## 2 d21          -0.891      41.4      42.2 liver_wtbw HF     LF        12    12
## # ℹ 9 more variables: statistic <dbl>, p <dbl>, df <dbl>, conf.low <dbl>,
## #   conf.high <dbl>, method <chr>, alternative <chr>, p.signif <chr>,
## #   p.format <chr>
## Calculate positions statistics on plot
stat.in <- stat.in %>% add_xy_position(x = OUTER.VAR, dodge = 0.8)
stat.out <- stat.out %>% add_xy_position(x = OUTER.VAR)
stat.out$y.position <- max(stat.in$y.position)*1.1

# Create plot
p <- ggboxplot(dat.clean, x = OUTER.VAR, y = "liver_norm",
               color = "feedtreat",
               fill = "feedtreat",
               add = "jitter",
               add.params = list(size = 1),
               panel.labs = list("dissection" = c("Day 8","Day 21")),
               facet.by = "dissection") +
  theme_pubr(legend = "top") +
  scale_fill_manual(breaks = dat.clean$treatment, values = params$COL1) +
  scale_color_manual(breaks = dat.clean$treatment, values = c("HF_CTRL" = "black","HF_PFOS" = "black","LF_CTRL" = "black","LF_PFOS" = "black")) +
  scale_x_discrete(name = "Feed") +
  guides(color = FALSE) +
  theme(axis.title.x = element_blank())
p

# Add p-values for inner and outer analysis to plot
p.stat <- p + 
  stat_pvalue_manual(stat.in, label = "p.signif", tip.length = 0, hide.ns = TRUE,  color = "red", y.position = 1.28) +
  stat_pvalue_manual(stat.out, label = "p.signif", tip.length = 0, hide.ns = TRUE) +
  scale_y_continuous(name ="Percentage from mean HF-CTRL", expand = expansion(mult = c(0.01, 0.1)), limits = c(0.85,1.3), breaks = seq(0.8,1.3,0.1), labels = function(x) paste0(x*100-100, "%"))
p.stat

### Create output plot
suppressMessages(ggsave(filename = paste0("plots/animal_data/weight/liver_weight_nested_",OUTER.VAR,"_",INNER.VAR,".png"), plot = p.stat, device = "png", units = "mm", width = 130, height = 80, dpi = 300))
suppressMessages(ggsave(filename = paste0("plots/animal_data/weight/liver_weight_nested_",OUTER.VAR,"_",INNER.VAR,".pdf"), plot = p.stat, device = "pdf", units = "mm", width = 130, height = 80, dpi = 300))

# clear the environment and release memory
rm(list = ls(all.names = TRUE)[ls(all.names = TRUE) != "params"])
invisible(gc())

6 PFOS QUANTITATIVE DATA

Following section handles data analysis of PFOS samples. Biological samples were extracted in acetonitrile and run on a LC-HRMS system using UPLC (Vanquish Flex, Thermo Fisher Scientific) coupled to an Orbitrap HRMS (Exploris 120, Thermo Fisher Scientific) against a linear PFOS standard curve and with internal MPFOS standard. Data presented here in ug/mL, ug/g and total mg are calculated from dilution factors and the original sample concentrations.

6.1 SYSTEMIC MEASUREMENTS

Following systemic measurements are analysed and presented here:

  • Blood serum from day 8, 16, and 21
  • Brain tissue from dissection days 8 and 21
  • Liver tissue from dissection days 8 and 21

6.1.1 Blood serum

6.1.1.1 ug/mL

6.1.1.1.1 Prepare data
# Load data
load("R_objects/animal_data.Rdata")
params <- readRDS("R_objects/animal_params.RDS")

# Set parameters
PREDICTOR <- c("feed","day")
OUTCOME <- "conc"
SUBJECT <- "rat_name"

# Create data frame for data representation
dat.clean <- dat %>% select(rat_name, feed, treatment, feedtreat, dissection, bw_8, bw_21, bloodvol_8, bloodvol_16, bloodvol_21, pfos_total_mg, pfos_serum8_ugml, pfos_serum16_ugml, pfos_serum21_ugml) %>%
  pivot_longer(., cols = c(pfos_serum8_ugml, pfos_serum16_ugml, pfos_serum21_ugml), names_to = "pfos_day", values_to = "conc")

# Create column for day of sampling
dat.clean <- dat.clean %>% 
  mutate("day" = case_when(pfos_day == "pfos_serum8_ugml" ~ "d08",
                           pfos_day == "pfos_serum16_ugml" ~ "d16",
                           pfos_day == "pfos_serum21_ugml" ~ "d21"))
  
# Order dataframe for analysis
dat.clean <- dat.clean[order(dat.clean$day),]

# Remove rows with NA
dat.clean <- subset(dat.clean, !is.na(conc))
# Subset to only PFOS groups
dat.clean <- subset(dat.clean, dat.clean$treatment == "PFOS")
dat.clean
## # A tibble: 48 × 14
##    rat_name feed  treatment feedtreat dissection  bw_8 bw_21 bloodvol_8
##    <chr>    <chr> <chr>     <chr>     <chr>      <int> <dbl>      <dbl>
##  1 R13      LF    PFOS      LF_PFOS   d08          373   NA        23.9
##  2 R14      LF    PFOS      LF_PFOS   d08          390   NA        25.0
##  3 R15      LF    PFOS      LF_PFOS   d08          341   NA        21.8
##  4 R16      LF    PFOS      LF_PFOS   d08          299   NA        19.1
##  5 R17      LF    PFOS      LF_PFOS   d08          322   NA        20.6
##  6 R18      LF    PFOS      LF_PFOS   d08          423   NA        27.1
##  7 R19      LF    PFOS      LF_PFOS   d21          344  403.       22.0
##  8 R20      LF    PFOS      LF_PFOS   d21          357  442.       22.8
##  9 R21      LF    PFOS      LF_PFOS   d21          342  422.       21.9
## 10 R22      LF    PFOS      LF_PFOS   d21          349  432.       22.3
## # ℹ 38 more rows
## # ℹ 6 more variables: bloodvol_16 <dbl>, bloodvol_21 <dbl>,
## #   pfos_total_mg <dbl>, pfos_day <chr>, conc <dbl>, day <chr>
# Create formula
PREDICTOR.F <- ifelse(length(PREDICTOR) > 1, paste(PREDICTOR, collapse = "*"), PREDICTOR)
FORMULA <- as.formula(paste(OUTCOME,PREDICTOR.F, sep = " ~ "))

# Summary samples in groups
dat.clean %>% group_by(across(all_of(PREDICTOR))) %>% get_summary_stats(!!sym(OUTCOME), type = "full") %>% select("feed","day","n","min","max","mean","sd","se")
## # A tibble: 6 × 8
##   feed  day       n   min   max  mean    sd    se
##   <chr> <chr> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
## 1 HF    d08      12  46.8  64.0  55.9  5.17  1.49
## 2 HF    d16       6  39.6  50.4  45.7  3.78  1.54
## 3 HF    d21       6  34.2  43.4  40.0  3.69  1.51
## 4 LF    d08      12  51.5  66.7  58.4  4.37  1.26
## 5 LF    d16       6  44.3  53.6  49.2  3.00  1.23
## 6 LF    d21       6  36.3  51.1  42.3  5.12  2.09

Visualise

Create a boxplot of the data.

# Create plot
bxp <- dat.clean %>%
  ggboxplot(x = if_else(length(PREDICTOR) > 1, PREDICTOR[2],PREDICTOR[1]),
            y = OUTCOME,
            color = PREDICTOR[1],
            facet.by = if(length(PREDICTOR) == 3) PREDICTOR[3],
            palette = "jco")
bxp

Assumptions and preliminary tests

The ANOVA tests assume the following characteristics about the data:

  • Independence of the observations. Each subject should belong to only one group. There is no relationship between the observations in each group.
    This is already done for the whole project

  • No significant outliers in the two groups

  • Normality. the data for each group should be approximately normally distributed.

  • Homogeneity of variances. the variance of the outcome variable should be equal in each group.

In this section, we’ll perform some preliminary tests to check whether these assumptions are met.

Identify outliers
Outliers can be easily identified using boxplot methods, implemented in the R function identify_outliers() [rstatix package].

# Test for outliers
dat.clean %>% 
  group_by(across(all_of(PREDICTOR))) %>% 
  identify_outliers(!!sym(OUTCOME))
## # A tibble: 3 × 16
##   feed  day   rat_name treatment feedtreat dissection  bw_8 bw_21 bloodvol_8
##   <chr> <chr> <chr>    <chr>     <chr>     <chr>      <int> <dbl>      <dbl>
## 1 LF    d16   R23      PFOS      LF_PFOS   d21          337  442.       21.6
## 2 LF    d16   R24      PFOS      LF_PFOS   d21          306  380.       19.6
## 3 LF    d21   R23      PFOS      LF_PFOS   d21          337  442.       21.6
## # ℹ 7 more variables: bloodvol_16 <dbl>, bloodvol_21 <dbl>,
## #   pfos_total_mg <dbl>, pfos_day <chr>, conc <dbl>, is.outlier <lgl>,
## #   is.extreme <lgl>

Data contains three outliers. These do not change the outcome of the analysis and are left in.

Check normality
QQ plot and Shapiro-Wilk test of normality are used to analyze the model residuals.

# Build the linear model
model  <- lm(FORMULA, data = dat.clean)
# Create a QQ plot of residuals
ggqqplot(residuals(model))

# Compute Shapiro-Wilk test of normality
shapiro_test(residuals(model))
## # A tibble: 1 × 3
##   variable         statistic p.value
##   <chr>                <dbl>   <dbl>
## 1 residuals(model)     0.988   0.885

Data is Normally distributed.

Test homogneity of variance assumption
1. The residuals versus fits plot can be used to check the homogeneity of variances.

plot(model, 1)

  1. It’s also possible to use the Levene’s test to check the homogeneity of variances:
dat.clean %>% levene_test(FORMULA)
## # A tibble: 1 × 4
##     df1   df2 statistic     p
##   <int> <int>     <dbl> <dbl>
## 1     5    42     0.604 0.697
# Save result
EQUAL.VAR <- dat.clean %>% levene_test(FORMULA) %>% pull(p) > 0.05

If the p-value of the Levene’s test is significant, it suggests that there is a significant difference between the variances of the two groups. In such case we should use Welch t-test, which doesn’t assume the equality of the two variances (var.equal=FALSE). If the Levene’s test is non-significant we can perform a Student t-test (var.equal=TRUE).

This all shows that normalised cecum weight data has two outliers, is normally distribution and has equal variance. Therefore we use a one-way ANOVA test with Tukey’s honest significance test to test data for all factors of interest, while using unpaired t-test for pairwise comparisons.

6.1.1.1.2 ANOVA One-Way test

Perform test

If we had equality of variance we can now run a one-way ANOVA tests anova_test() (if we have equal variance) or a welch_anova_test() (if variance vary).

if(EQUAL.VAR) {
  res.aov <- dat.clean %>% anova_test(FORMULA, dv = conc, between = feed, within = day, wid = rat_name)
  res.aov
} else {
  res.aov <- dat.clean %>% welch_anova_test(FORMULA)
  res.aov
}
## ANOVA Table (type II tests)
## 
##     Effect DFn DFd      F        p p<.05   ges
## 1     feed   1  42  4.452 4.10e-02     * 0.096
## 2      day   2  42 56.842 1.13e-12     * 0.730
## 3 feed:day   2  42  0.073 9.30e-01       0.003

ANOVA shows that day and feed have significant effects on the overall data.

Perform posthoc test

A significant one-way ANOVA is generally followed up by Tukey post-hoc tests to perform multiple pairwise comparisons between groups. When running relaxed Welch one-way test, the Games-Howell post hoc test or pairwise t-tests (with no assumption of equal variances) can be used to compare all possible combinations of group differences.

if(EQUAL.VAR) {
  pwc <- dat.clean %>% tukey_hsd(FORMULA)
  pwc
} else {
  pwc <- dat.clean %>% games_howell_test(FORMULA)
  pwc
}
## # A tibble: 19 × 9
##    term     group1 group2 null.value estimate conf.low conf.high    p.adj
##  * <chr>    <chr>  <chr>       <dbl>    <dbl>    <dbl>     <dbl>    <dbl>
##  1 feed     HF     LF              0     2.69    0.117     5.27  4.09e- 2
##  2 day      d08    d16             0    -9.71  -13.5      -5.91  5.78e- 7
##  3 day      d08    d21             0   -16.0   -19.8     -12.2   2.73e-12
##  4 day      d16    d21             0    -6.29  -10.7      -1.90  3.27e- 3
##  5 feed:day HF:d08 LF:d08          0     2.50   -2.89      7.88  7.35e- 1
##  6 feed:day HF:d08 HF:d16          0   -10.2   -16.8      -3.62  4.82e- 4
##  7 feed:day HF:d08 LF:d16          0    -6.70  -13.3      -0.106 4.45e- 2
##  8 feed:day HF:d08 HF:d21          0   -15.9   -22.5      -9.28  1.16e- 7
##  9 feed:day HF:d08 LF:d21          0   -13.6   -20.2      -7.03  3.31e- 6
## 10 feed:day LF:d08 HF:d16          0   -12.7   -19.3      -6.12  1.27e- 5
## 11 feed:day LF:d08 LF:d16          0    -9.20  -15.8      -2.60  1.98e- 3
## 12 feed:day LF:d08 HF:d21          0   -18.4   -25.0     -11.8   3.01e- 9
## 13 feed:day LF:d08 LF:d21          0   -16.1   -22.7      -9.52  8.04e- 8
## 14 feed:day HF:d16 LF:d16          0     3.52   -4.10     11.1   7.39e- 1
## 15 feed:day HF:d16 HF:d21          0    -5.65  -13.3       1.96  2.52e- 1
## 16 feed:day HF:d16 LF:d21          0    -3.40  -11.0       4.21  7.65e- 1
## 17 feed:day LF:d16 HF:d21          0    -9.17  -16.8      -1.56  1.03e- 2
## 18 feed:day LF:d16 LF:d21          0    -6.92  -14.5       0.695 9.39e- 2
## 19 feed:day HF:d21 LF:d21          0     2.25   -5.36      9.87  9.49e- 1
## # ℹ 1 more variable: p.adj.signif <chr>

Posthoc analysis however show significant differences between to be between overall treatment, overall dissection days, as well as cross-day comparisons.

6.1.1.1.3 Nested analysis and Figure
# Set variables for inner and outer analyses
INNER.VAR <- "feed"
OUTER.VAR <- "day"

# Statistics costumed for facet plotting
## T-test comparison for inner variable
stat.in <- dat.clean %>%
  group_by(day) %>%
  t_test(conc ~ feed, 
         paired = FALSE, var.equal = EQUAL.VAR, 
         detailed = TRUE, alternative = "two.sided") %>%
  add_significance() %>%
  p_format("p", accuracy = 0.001, trailing.zero = TRUE, new.col = TRUE)
stat.in
## # A tibble: 3 × 18
##   day   estimate estimate1 estimate2 .y.   group1 group2    n1    n2 statistic
## * <chr>    <dbl>     <dbl>     <dbl> <chr> <chr>  <chr>  <int> <int>     <dbl>
## 1 d08      -2.50      55.9      58.4 conc  HF     LF        12    12    -1.28 
## 2 d16      -3.52      45.7      49.2 conc  HF     LF         6     6    -1.78 
## 3 d21      -2.25      40.0      42.3 conc  HF     LF         6     6    -0.874
## # ℹ 8 more variables: p <dbl>, df <dbl>, conf.low <dbl>, conf.high <dbl>,
## #   method <chr>, alternative <chr>, p.signif <chr>, p.format <chr>
## ANOVA comparison for outer variable
stat.out <- dat.clean %>%
  anova_test(conc ~ day) %>%
  add_significance() %>%
  p_format("p", accuracy = 0.001, trailing.zero = TRUE, new.col = TRUE)
stat.out
## ANOVA Table (type II tests)
## 
##   Effect DFn DFd      F        p p<.05   ges p.signif p.format
## 1    day   2  45 54.892 8.48e-13     * 0.709     ****   <0.001
pwc2 <- dat.clean %>%
  tukey_hsd(conc ~ day) %>%
  add_significance() %>%
  p_format("p.adj", accuracy = 0.001, trailing.zero = TRUE, new.col = TRUE)
pwc2
## # A tibble: 3 × 10
##   term  group1 group2 null.value estimate conf.low conf.high    p.adj
## * <chr> <chr>  <chr>       <dbl>    <dbl>    <dbl>     <dbl>    <dbl>
## 1 day   d08    d16             0    -9.71    -13.6     -5.86 6.42e- 7
## 2 day   d08    d21             0   -16.0     -19.8    -12.1  1.55e-12
## 3 day   d16    d21             0    -6.29    -10.7     -1.84 3.71e- 3
## # ℹ 2 more variables: p.adj.signif <chr>, p.adj.format <chr>
## Calculate positions statistics on plot
stat.in <- stat.in %>% add_xy_position(x = "day", dodge = 0.8)
pwc2 <- pwc2 %>% add_xy_position(x = "day")
pwc2$y.position <- max(stat.in$y.position)*1.1

# Calculate slope and intercept for each group
reg_lines <- dat.clean %>%
  group_by(feed) %>%
  summarize(slope = coef(lm(conc ~ day))[2],
            intercept = coef(lm(conc ~ day))[1])

# Statistical test of regression slopes from all d21 rats
rls <- dat.clean %>% subset(dissection == "d21") %>%
  group_by(rat_name) %>%
  summarize(slope = coef(lm(conc ~ day))[2],
            intercept = coef(lm(conc ~ day))[1])
rls$group <- rep(c("LF","HF"), each = 6)

stat.rls <- rls %>%
  t_test(slope ~ group,
         paired = FALSE, var.equal = EQUAL.VAR, 
         detailed = TRUE, alternative = "two.sided") %>%
  add_significance() %>%
  p_format("p", accuracy = 0.001, trailing.zero = TRUE, new.col = TRUE)
stat.rls
## # A tibble: 1 × 17
##   estimate estimate1 estimate2 .y.   group1 group2    n1    n2 statistic     p
## *    <dbl>     <dbl>     <dbl> <chr> <chr>  <chr>  <int> <int>     <dbl> <dbl>
## 1   -0.797     -8.36     -7.56 slope HF     LF         6     6    -0.460 0.656
## # ℹ 7 more variables: df <dbl>, conf.low <dbl>, conf.high <dbl>, method <chr>,
## #   alternative <chr>, p.signif <chr>, p.format <chr>
# Create ggboxplot with regression lines for feed types and slope values as text
p <- ggboxplot(dat.clean, x = "day", y = "conc",
               fill = "feed",
               color = "feed",
               add = "jitter",
               add.params = list(size = 1)) +
  theme_pubr(legend = "top") +
  scale_fill_manual(values = params$COLFEED, name = "Feed") +
  scale_color_manual(breaks = dat.clean$feed, values = c("LF" = "#222222","HF" = "#222222", "HF_PFOS" = "#004400","LF_PFOS" = "#000066")) +
  scale_x_discrete(name = "Day", labels = c("Day 8","Day 16","Day 21")) +
  guides(color = FALSE) +
  theme(axis.title.x=element_blank()) +
  geom_smooth(aes(group = feed, color = feedtreat), method = "lm", se = FALSE, size = 0.5, fullrange = TRUE, linetype = "dashed") +
  annotate("text", x = 3, y = 65, label = paste("HF: ",round(reg_lines$slope[1],2)," µg/mL/day")) +
  annotate("text", x = 3, y = 62, label = paste("LF: ",round(reg_lines$slope[2],2)," µg/mL/day"))

# Add statistics
p.stat <- p + 
  stat_pvalue_manual(stat.in, label = "p.signif", tip.length = 0, hide.ns = TRUE, color = "red") +
  stat_pvalue_manual(pwc2, label = "p.adj.signif", tip.length = 0, hide.ns = TRUE, y.position = c(73,76,70), limits = c(30,80),breaks = seq(30,80,10)) +
  scale_y_continuous(name ="Serum PFOS ug / mL", expand = expansion(mult = c(0.01, 0.1)))
# View the final plot
p.stat
## `geom_smooth()` using formula = 'y ~ x'

# Save plot as Rdata element
p.blood <- p.stat
save(p.blood, file = "plots/animal_data/pfos/systemic_blood_figure.Rdata")

# Save plots
suppressMessages(ggsave(filename = paste0("plots/animal_data/pfos/systemic_blood_",OUTCOME,".png"), plot = p.stat, device = "png", units = "mm", dpi = 300, height = 100, width = 120))
suppressMessages(ggsave(filename = paste0("plots/animal_data/pfos/systemic_blood_",OUTCOME,".pdf"), plot = p.stat, device = "pdf", units = "mm", dpi = 300, height = 100, width = 120))

# Save output from ANOVA and post hoc
list.serum <- list(pwc = pwc,res.aov = res.aov)
for (i in names(list.serum)) {
  write.csv(list.serum[[i]], file = paste0("plots/animal_data/pfos/serum_",i,"_results.csv"))
}

# clear the environment and release memory
rm(list = ls(all.names = TRUE)[ls(all.names = TRUE) != "params"])
invisible(gc())

6.1.2 Brain tissue

6.1.2.1 ug/g

6.1.2.1.1 Prepare data

This section sets the variables to be used and prepares the data if necessary.

# load data 
load("R_objects/animal_data.Rdata")
params <- readRDS("R_objects/animal_params.RDS")

# Set names of variables
PREDICTOR <- "dissection"
OUTCOME <- "pfos_brain_ugg"
SUBJECT <- "rat_name"

# Subset to a specific varible
dat.clean <- subset(dat, treatment == "PFOS" & pfos_brain_ugg != "")

# Remove rows with NA
dat.clean <- subset(dat.clean, !is.na(pfos_brain_ugg))

# Will you run a paired test? (set variable to `TRUE` or `FALSE`)
PAIRED <- FALSE

# Create formula
FORMULA <- as.formula(paste(OUTCOME, PREDICTOR, sep = "~"))

# Summary samples in groups
dat.clean %>% group_by(across(all_of(c("feed","dissection")))) %>% get_summary_stats(!!sym(OUTCOME), type = "full") %>% select("feed","dissection","n","min","max","mean","sd","se")
## # A tibble: 4 × 8
##   feed  dissection     n   min   max  mean    sd    se
##   <chr> <chr>      <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
## 1 HF    d08            5  3.90  5.08  4.39 0.429 0.192
## 2 HF    d21            5  1.62  2.61  2.12 0.406 0.182
## 3 LF    d08            5  3.06  4.66  3.92 0.596 0.267
## 4 LF    d21            6  1.46  2.91  2.47 0.532 0.217
# Sort data for paired test
if (PAIRED) {
  # Order data
  dat.clean <- arrange(dat.clean, !!sym(SUBJECT))
  
  # Remove unpaired samples
  dat.clean <- dat.clean %>% 
    group_by(!!sym(SUBJECT)) %>%
    filter(n() != 1) %>%
    droplevels() %>% 
    ungroup()
}
# identify outliers
dat.clean %>%
  group_by(!!sym(PREDICTOR)) %>%
  identify_outliers(!!sym(OUTCOME))
## # A tibble: 2 × 79
##   dissection rat_org rat_name  cage treatment feed  feedtreat feedtreatday
##   <chr>      <chr>   <chr>    <int> <chr>     <chr> <chr>     <chr>       
## 1 d08        R18     R18          9 PFOS      LF    LF_PFOS   LF_PFOS_d08 
## 2 d08        R43     R41         22 PFOS      HF    HF_PFOS   HF_PFOS_d08 
## # ℹ 71 more variables: bw_minus18 <dbl>, bw_0 <dbl>, bw_1 <int>, bw_2 <int>,
## #   bw_3 <int>, bw_4 <int>, bw_5 <int>, bw_6 <int>, bw_7 <int>, bw_8 <int>,
## #   bw_12 <dbl>, bw_16 <dbl>, bw_21 <dbl>, bw_gain08 <dbl>, bw_gain021 <dbl>,
## #   bw_gain821 <dbl>, bloodvol_8 <dbl>, bloodvol_16 <dbl>, bloodvol_21 <dbl>,
## #   cecum_wt <dbl>, cecum_wtbw <dbl>, cecum_norm <dbl>, liver_wt <dbl>,
## #   liver_wtbw <dbl>, liver_norm <dbl>, brain_wt_estimate <dbl>,
## #   transit_0 <int>, transit_7 <int>, transit_20 <int>, dose_total_ml <dbl>, …
# Create QQplot
ggqqplot(dat.clean, x = OUTCOME, facet.by = PREDICTOR)

# Run test
dat.clean %>% levene_test(FORMULA)
## Warning in leveneTest.default(y = y, group = group, ...): group coerced to
## factor.
## # A tibble: 1 × 4
##     df1   df2 statistic     p
##   <int> <int>     <dbl> <dbl>
## 1     1    19    0.0112 0.917
# Save output
EQUAL.VAR <- dat.clean %>% levene_test(FORMULA) %>% pull(p) > 0.05
## Warning in leveneTest.default(y = y, group = group, ...): group coerced to
## factor.

No outliers were identified. Data is normally distributed and has equal variance. Hence we use t-test.

6.1.2.1.2 PERFORM TEST

T-test
We are now ready to perform the test

stat.test <- dat.clean %>% 
  t_test(FORMULA,
         var.equal = EQUAL.VAR,
         detailed = TRUE,
         paired = FALSE,
         alternative = "two.sided") %>%
  add_significance()
stat.test
## # A tibble: 1 × 16
##   estimate estimate1 estimate2 .y.   group1 group2    n1    n2 statistic       p
##      <dbl>     <dbl>     <dbl> <chr> <chr>  <chr>  <int> <int>     <dbl>   <dbl>
## 1     1.84      4.16      2.31 pfos… d08    d21       10    11      8.13 1.31e-7
## # ℹ 6 more variables: df <dbl>, conf.low <dbl>, conf.high <dbl>, method <chr>,
## #   alternative <chr>, p.signif <chr>

Effect size
The effect size is calculated as Cohen’s D

dat.clean %>% cohens_d(FORMULA, 
                       var.equal = EQUAL.VAR,
                       paired = FALSE)
## # A tibble: 1 × 7
##   .y.            group1 group2 effsize    n1    n2 magnitude
## * <chr>          <chr>  <chr>    <dbl> <int> <int> <ord>    
## 1 pfos_brain_ugg d08    d21       3.55    10    11 large
6.1.2.1.3 Nested analysis and Figure
# Set variables for inner and outer analyses
INNER.VAR <- "feed"
OUTER.VAR <- "dissection"

# Statistics costumed for facet plotting
## Pairwise comparison for inner variable
stat.in <- dat.clean %>%
  group_by(dissection) %>%
  t_test(pfos_brain_ugg ~ feed, 
         paired = FALSE, var.equal = EQUAL.VAR, 
         detailed = TRUE, alternative = "two.sided") %>%
  add_significance() %>%
  p_format("p", accuracy = 0.001, trailing.zero = TRUE, new.col = TRUE)
stat.in
## # A tibble: 2 × 18
##   dissection estimate estimate1 estimate2 .y.          group1 group2    n1    n2
## * <chr>         <dbl>     <dbl>     <dbl> <chr>        <chr>  <chr>  <int> <int>
## 1 d08           0.467      4.39      3.92 pfos_brain_… HF     LF         5     5
## 2 d21          -0.352      2.12      2.47 pfos_brain_… HF     LF         5     6
## # ℹ 9 more variables: statistic <dbl>, p <dbl>, df <dbl>, conf.low <dbl>,
## #   conf.high <dbl>, method <chr>, alternative <chr>, p.signif <chr>,
## #   p.format <chr>
## Pairwise comparison for outer variable
stat.out <- dat.clean %>%
  t_test(pfos_brain_ugg ~ dissection,
         paired = FALSE, var.equal = EQUAL.VAR,
         detailed = TRUE, alternative = "two.sided") %>%
  add_significance() %>%
  p_format("p", accuracy = 0.001, trailing.zero = TRUE, new.col = TRUE)
stat.out
## # A tibble: 1 × 17
##   estimate estimate1 estimate2 .y.   group1 group2    n1    n2 statistic       p
## *    <dbl>     <dbl>     <dbl> <chr> <chr>  <chr>  <int> <int>     <dbl>   <dbl>
## 1     1.84      4.16      2.31 pfos… d08    d21       10    11      8.13 1.31e-7
## # ℹ 7 more variables: df <dbl>, conf.low <dbl>, conf.high <dbl>, method <chr>,
## #   alternative <chr>, p.signif <chr>, p.format <chr>
## Calculate positions statistics on plot
stat.in <- stat.in %>% add_xy_position(x = "dissection", dodge = 0.8)
stat.out <- stat.out %>% add_xy_position(x = "dissection")
stat.out$y.position <- max(stat.in$y.position)*1.1

# Calculate slope and intercept for each group
reg_lines <- dat.clean %>%
  group_by(feed) %>%
  summarize(slope = coef(lm(pfos_brain_ugg ~ dissection))[2],
            intercept = coef(lm(pfos_brain_ugg ~ dissection))[1])

# Create ggboxplot with regression lines for feed types and slope values as text
p <- ggboxplot(dat.clean, x = "dissection", y = "pfos_brain_ugg",
               fill = "feed",
               color = "feed",
               add = "jitter",
               add.params = list(size = 1)) +
  theme_pubr(legend = "top") +
  scale_fill_manual(values = params$COLFEED, name = "Feed") +
  scale_color_manual(breaks = dat.clean$feed, values = c("LF" = "#222222","HF" = "#222222", "HF_PFOS" = "#004400","LF_PFOS" = "#000066")) +
  scale_x_discrete(name = "Day", labels = c("Day 8","Day 21")) +
  guides(color = FALSE) +
  theme(axis.title.x=element_blank()) +
  geom_smooth(aes(group = feed, color = feedtreat), method = "lm", se = FALSE, size = 0.5, fullrange = TRUE, linetype = "dashed")

# Add statistics
p.stat <- p + 
  stat_pvalue_manual(stat.in, label = "p.signif", tip.length = 0, hide.ns = TRUE, color = "red") +
  stat_pvalue_manual(stat.out, label = "p.signif", tip.length = 0, hide.ns = TRUE) +
  scale_y_continuous(name ="Brain PFOS ug / g", expand = expansion(mult = c(0.01, 0.1)), limits = c(1,6), breaks = seq(1,6,1)) +
  annotate("text", x = 2, y = 5.1, size = 3, label = paste0("HF: ",round(reg_lines$slope[1],2)," µg/g/day")) +
  annotate("text", x = 2, y = 4.7, size = 3, label = paste0("LF: ",round(reg_lines$slope[2],2)," µg/g/day"))
# View the final plot
p.stat
## `geom_smooth()` using formula = 'y ~ x'

# Save plot as Rdata element
p.brain <- p.stat
save(p.brain, file = "plots/animal_data/pfos/systemic_brain_figure.Rdata")

# Save plot as PNG and PDF formats
suppressMessages(ggsave(filename = paste0("plots/animal_data/pfos/systemic_brain_",OUTCOME,".png"), plot = p.stat, device = "png", units = "mm", dpi = 300, height = 100, width = 90))
suppressMessages(ggsave(filename = paste0("plots/animal_data/pfos/systemic_brain_",OUTCOME,".pdf"), plot = p.stat, device = "pdf", units = "mm", dpi = 300, height = 100, width = 90))

# clear the environment and release memory
rm(list = ls(all.names = TRUE)[ls(all.names = TRUE) != "params"])
invisible(gc())

6.1.3 Liver tissue

6.1.3.1 ug/g

6.1.3.1.1 Prepare data

This section sets the variables to be used and prepares the data if necessary.

# load data 
load("R_objects/animal_data.Rdata")
params <- readRDS("R_objects/animal_params.RDS")

# Set names of variables
PREDICTOR <- "dissection"
OUTCOME <- "pfos_liver_ugg"
SUBJECT <- "rat_name"

# Subset to a specific varible
dat.clean <- subset(dat, treatment == "PFOS" & !is.na(pfos_liver_ugg))

# Will yoou run a paired test? (set variable to `TRUE` or `FALSE`)
PAIRED <- FALSE

# Create formula
FORMULA <- as.formula(paste(OUTCOME, PREDICTOR, sep = "~"))

# Summary samples in groups
dat.clean %>% group_by(across(all_of(c("feed","dissection")))) %>% get_summary_stats(!!sym(OUTCOME), type = "full") %>% select("feed","dissection","n","min","max","mean","sd","se")
## # A tibble: 4 × 8
##   feed  dissection     n   min   max  mean    sd    se
##   <chr> <chr>      <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
## 1 HF    d08            6  185.  204.  194.  7.56  3.09
## 2 HF    d21            6  138.  156.  147.  6.58  2.69
## 3 LF    d08            6  156.  187.  170. 12.0   4.89
## 4 LF    d21            6  136.  157.  145.  8.16  3.33
# Sort data for paired test
if (PAIRED) {
  # Order data
  dat.clean <- arrange(dat.clean, !!sym(SUBJECT))
  
  # Remove unpaired samples
  dat.clean <- dat.clean %>% 
    group_by(!!sym(SUBJECT)) %>%
    filter(n() != 1) %>%
    arrange(!!sym(PREDICTOR), !!sym(SUBJECT)) %>%
    droplevels() %>% 
    ungroup()
}

# Summary samples in groups
dat.clean %>% group_by(across(all_of("feedtreatday"))) %>% get_summary_stats(!!sym(OUTCOME), type = "mean_sd")
## # A tibble: 4 × 5
##   feedtreatday variable           n  mean    sd
##   <chr>        <fct>          <dbl> <dbl> <dbl>
## 1 HF_PFOS_d08  pfos_liver_ugg     6  194.  7.56
## 2 HF_PFOS_d21  pfos_liver_ugg     6  147.  6.58
## 3 LF_PFOS_d08  pfos_liver_ugg     6  170. 12.0 
## 4 LF_PFOS_d21  pfos_liver_ugg     6  145.  8.16
# identify outliers
dat.clean %>%
  group_by(!!sym(PREDICTOR)) %>%
  identify_outliers(!!sym(OUTCOME))
##  [1] dissection        rat_org           rat_name          cage             
##  [5] treatment         feed              feedtreat         feedtreatday     
##  [9] bw_minus18        bw_0              bw_1              bw_2             
## [13] bw_3              bw_4              bw_5              bw_6             
## [17] bw_7              bw_8              bw_12             bw_16            
## [21] bw_21             bw_gain08         bw_gain021        bw_gain821       
## [25] bloodvol_8        bloodvol_16       bloodvol_21       cecum_wt         
## [29] cecum_wtbw        cecum_norm        liver_wt          liver_wtbw       
## [33] liver_norm        brain_wt_estimate transit_0         transit_7        
## [37] transit_20        dose_total_ml     pfos_total_mg     pfos_liver_ugg   
## [41] pfos_liver_mg     pfos_liver8_pct   pfos_brain_ugg    pfos_brain_uggbw 
## [45] pfos_brain_ug     pfos_brain_pct    pfos_serum8_ugml  pfos_serum8_mg   
## [49] pfos_serum8_pct   pfos_serum16_ugml pfos_serum16_mg   pfos_serum16_pct 
## [53] pfos_serum21_ugml pfos_serum21_mg   pfos_serum21_pct  pfos_feces8_ugg  
## [57] pfos_feces12_ugg  pfos_feces16_ugg  pfos_feces21_ugg  pfos_cecum_ugg   
## [61] pfos_urine8_ugml  pfos_urine16_ugml pfos_urine21_ugml pH_je_up         
## [65] pH_je_down        pH_ileum          pH_cecum          acetic           
## [69] formic            propanoic         m2_propanoic      butanoic         
## [73] m3_butanoic       pentanoic         m4_pentanoic      hexanoic         
## [77] heptanoic         is.outlier        is.extreme       
## <0 rækker> (eller 0-længde row.names)
# Run Shapiro test
dat.clean %>% 
  group_by(!!sym(PREDICTOR)) %>%
  shapiro_test(!!sym(OUTCOME))
## # A tibble: 2 × 4
##   dissection variable       statistic     p
##   <chr>      <chr>              <dbl> <dbl>
## 1 d08        pfos_liver_ugg     0.942 0.518
## 2 d21        pfos_liver_ugg     0.936 0.448
# Create QQplot
ggqqplot(dat.clean, x = OUTCOME, facet.by = PREDICTOR)

# Run test
dat.clean %>% levene_test(FORMULA)
## Warning in leveneTest.default(y = y, group = group, ...): group coerced to
## factor.
## # A tibble: 1 × 4
##     df1   df2 statistic      p
##   <int> <int>     <dbl>  <dbl>
## 1     1    22      4.60 0.0434
# Save output
EQUAL.VAR <- dat.clean %>% levene_test(FORMULA) %>% pull(p) > 0.05
## Warning in leveneTest.default(y = y, group = group, ...): group coerced to
## factor.

No outliers were identified. Data is normally distributed and has equal variance. Hence we use t-test.

6.1.3.1.2 PERFORM TEST

T-test
We are now ready to perform the test

stat.test <- dat.clean %>% 
  t_test(FORMULA,
         var.equal = EQUAL.VAR,
         detailed = TRUE,
         paired = FALSE,
         alternative = "two.sided") %>%
  add_significance()
stat.test
## # A tibble: 1 × 16
##   estimate estimate1 estimate2 .y.   group1 group2    n1    n2 statistic       p
##      <dbl>     <dbl>     <dbl> <chr> <chr>  <chr>  <int> <int>     <dbl>   <dbl>
## 1     36.4      182.      146. pfos… d08    d21       12    12      7.24 2.63e-6
## # ℹ 6 more variables: df <dbl>, conf.low <dbl>, conf.high <dbl>, method <chr>,
## #   alternative <chr>, p.signif <chr>

Effect size
The effect size is calculated as Cohen’s D

dat.clean %>% cohens_d(FORMULA, 
                       var.equal = EQUAL.VAR,
                       paired = FALSE)
## # A tibble: 1 × 7
##   .y.            group1 group2 effsize    n1    n2 magnitude
## * <chr>          <chr>  <chr>    <dbl> <int> <int> <ord>    
## 1 pfos_liver_ugg d08    d21       2.96    12    12 large
6.1.3.1.3 Nested analysis and Figure
# Set variables for inner and outer analyses
INNER.VAR <- "feed"
OUTER.VAR <- "dissection"

# Statistics costumed for facet plotting
## Pairwise comparison for inner variable
stat.in <- dat.clean %>%
  group_by(dissection) %>%
  t_test(pfos_liver_ugg ~ feed, 
         paired = FALSE, var.equal = EQUAL.VAR, 
         detailed = TRUE, alternative = "two.sided") %>%
  add_significance() %>%
  p_format("p", accuracy = 0.001, trailing.zero = TRUE, new.col = TRUE)
stat.in
## # A tibble: 2 × 18
##   dissection estimate estimate1 estimate2 .y.          group1 group2    n1    n2
## * <chr>         <dbl>     <dbl>     <dbl> <chr>        <chr>  <chr>  <int> <int>
## 1 d08           24.3       194.      170. pfos_liver_… HF     LF         6     6
## 2 d21            1.43      147.      145. pfos_liver_… HF     LF         6     6
## # ℹ 9 more variables: statistic <dbl>, p <dbl>, df <dbl>, conf.low <dbl>,
## #   conf.high <dbl>, method <chr>, alternative <chr>, p.signif <chr>,
## #   p.format <chr>
## Pairwise comparison for outer variable
stat.out <- dat.clean %>%
  t_test(pfos_liver_ugg ~ dissection,
         paired = FALSE, var.equal = EQUAL.VAR,
         detailed = TRUE, alternative = "two.sided") %>%
  add_significance() %>%
  p_format("p", accuracy = 0.001, trailing.zero = TRUE, new.col = TRUE)
stat.out
## # A tibble: 1 × 17
##   estimate estimate1 estimate2 .y.   group1 group2    n1    n2 statistic       p
## *    <dbl>     <dbl>     <dbl> <chr> <chr>  <chr>  <int> <int>     <dbl>   <dbl>
## 1     36.4      182.      146. pfos… d08    d21       12    12      7.24 2.63e-6
## # ℹ 7 more variables: df <dbl>, conf.low <dbl>, conf.high <dbl>, method <chr>,
## #   alternative <chr>, p.signif <chr>, p.format <chr>
## Calculate positions statistics on plot
stat.in <- stat.in %>% add_xy_position(x = "dissection", dodge = 0.8)
stat.out <- stat.out %>% add_xy_position(x = "dissection")
stat.out$y.position <- max(stat.in$y.position)*1.1

# Calculate slope and intercept for each group
reg_lines <- dat.clean %>%
  group_by(feed) %>%
  summarize(slope = coef(lm(pfos_liver_ugg ~ dissection))[2],
            intercept = coef(lm(pfos_liver_ugg ~ dissection))[1])

# Create ggboxplot with regression lines for feed types and slope values as text
p <- ggboxplot(dat.clean, x = "dissection", y = "pfos_liver_ugg",
               fill = "feed",
               color = "feed",
               add = "jitter",
               add.params = list(size = 1)) +
  theme_pubr(legend = "top") +
  scale_fill_manual(values = params$COLFEED, name = "Feed") +
  scale_color_manual(breaks = dat.clean$feed, values = c("LF" = "#222222","HF" = "#222222", "HF_PFOS" = "#004400","LF_PFOS" = "#000066")) +
  scale_x_discrete(name = "Day", labels = c("Day 8","Day 21")) +
  guides(color = FALSE) +
  theme(axis.title.x=element_blank()) +
  geom_smooth(aes(group = feed, color = feedtreat), method = "lm", se = FALSE, size = 0.5, fullrange = TRUE, linetype = "dashed")

# Add statistics
p.stat <- p + 
  stat_pvalue_manual(stat.in, label = "p.signif", tip.length = 0, hide.ns = TRUE, color = "red") +
  stat_pvalue_manual(stat.out, label = "p.signif", tip.length = 0, hide.ns = TRUE, y.position = 225) +
  scale_y_continuous(name ="Liver PFOS ug / g", expand = expansion(mult = c(0.01, 0.1)), limits = c(125,225), breaks = seq(125,225,25)) +
  annotate("text", x = 2, y = 210, size = 3, label = paste0("HF: ",round(reg_lines$slope[1],2)," µg/g/day")) +
  annotate("text", x = 2, y = 203, size = 3, label = paste0("LF: ",round(reg_lines$slope[2],2)," µg/g/day"))
# View the final plot
p.stat
## `geom_smooth()` using formula = 'y ~ x'

# Save plot as Rdata element
p.liver <- p.stat
save(p.liver, file = "plots/animal_data/pfos/systemic_liverugg_figure.Rdata")

# Save plot as PNG and PDF formats
suppressMessages(ggsave(filename = paste0("plots/animal_data/pfos/systemic_liver_",OUTCOME,".png"), plot = p.stat, device = "png", units = "mm", dpi = 300, height = 100, width = 90))
suppressMessages(ggsave(filename = paste0("plots/animal_data/pfos/systemic_liver_",OUTCOME,".pdf"), plot = p.stat, device = "pdf", units = "mm", dpi = 300, height = 100, width = 90))

# clear the environment and release memory
rm(list = ls(all.names = TRUE)[ls(all.names = TRUE) != "params"])
invisible(gc())

6.1.3.2 mg total liver weight

6.1.3.2.1 Prepare data

This section sets the variables to be used and prepares the data if necessary.

# load data 
load("R_objects/animal_data.Rdata")
params <- readRDS("R_objects/animal_params.RDS")

# Set names of variables
PREDICTOR <- "dissection"#c("feed","day")
OUTCOME <- "pfos_liver_mg"
SUBJECT <- "rat_name"

# Subset to a specific varible
dat.clean <- subset(dat, treatment == "PFOS")

# Remove rows with NA
dat.clean <- subset(dat.clean, !is.na(pfos_liver_mg))

# Will yoou run a paired test? (set variable to `TRUE` or `FALSE`)
PAIRED <- FALSE

# Create formula
FORMULA <- as.formula(paste(OUTCOME, PREDICTOR, sep = "~"))

# Sort data for paired test
if (PAIRED) {
  # Order data
  dat.clean <- arrange(dat.clean, !!sym(SUBJECT))
  
  # Remove unpaired samples
  dat.clean <- dat.clean %>% 
    group_by(!!sym(SUBJECT)) %>%
    filter(n() != 1) %>%
    arrange(!!sym(PREDICTOR), !!sym(SUBJECT)) %>%
    droplevels() %>% 
    ungroup()
}
# Summary samples in groups
dat.clean %>% group_by(across(all_of("feedtreatday"))) %>% get_summary_stats(!!sym(OUTCOME), type = "mean_sd")
## # A tibble: 4 × 5
##   feedtreatday variable          n  mean    sd
##   <chr>        <fct>         <dbl> <dbl> <dbl>
## 1 HF_PFOS_d08  pfos_liver_mg     6  3.25 0.333
## 2 HF_PFOS_d21  pfos_liver_mg     6  2.62 0.215
## 3 LF_PFOS_d08  pfos_liver_mg     6  3.15 0.554
## 4 LF_PFOS_d21  pfos_liver_mg     6  2.58 0.373
# identify outliers
dat.clean %>%
  group_by(!!sym(PREDICTOR)) %>%
  identify_outliers(!!sym(OUTCOME))
##  [1] dissection        rat_org           rat_name          cage             
##  [5] treatment         feed              feedtreat         feedtreatday     
##  [9] bw_minus18        bw_0              bw_1              bw_2             
## [13] bw_3              bw_4              bw_5              bw_6             
## [17] bw_7              bw_8              bw_12             bw_16            
## [21] bw_21             bw_gain08         bw_gain021        bw_gain821       
## [25] bloodvol_8        bloodvol_16       bloodvol_21       cecum_wt         
## [29] cecum_wtbw        cecum_norm        liver_wt          liver_wtbw       
## [33] liver_norm        brain_wt_estimate transit_0         transit_7        
## [37] transit_20        dose_total_ml     pfos_total_mg     pfos_liver_ugg   
## [41] pfos_liver_mg     pfos_liver8_pct   pfos_brain_ugg    pfos_brain_uggbw 
## [45] pfos_brain_ug     pfos_brain_pct    pfos_serum8_ugml  pfos_serum8_mg   
## [49] pfos_serum8_pct   pfos_serum16_ugml pfos_serum16_mg   pfos_serum16_pct 
## [53] pfos_serum21_ugml pfos_serum21_mg   pfos_serum21_pct  pfos_feces8_ugg  
## [57] pfos_feces12_ugg  pfos_feces16_ugg  pfos_feces21_ugg  pfos_cecum_ugg   
## [61] pfos_urine8_ugml  pfos_urine16_ugml pfos_urine21_ugml pH_je_up         
## [65] pH_je_down        pH_ileum          pH_cecum          acetic           
## [69] formic            propanoic         m2_propanoic      butanoic         
## [73] m3_butanoic       pentanoic         m4_pentanoic      hexanoic         
## [77] heptanoic         is.outlier        is.extreme       
## <0 rækker> (eller 0-længde row.names)
# Run Shapiro test
dat.clean %>% 
  group_by(!!sym(PREDICTOR)) %>%
  shapiro_test(!!sym(OUTCOME))
## # A tibble: 2 × 4
##   dissection variable      statistic     p
##   <chr>      <chr>             <dbl> <dbl>
## 1 d08        pfos_liver_mg     0.943 0.536
## 2 d21        pfos_liver_mg     0.942 0.529
# Create QQplot
ggqqplot(dat.clean, x = OUTCOME, facet.by = PREDICTOR)

# Run test
dat.clean %>% levene_test(FORMULA)
## Warning in leveneTest.default(y = y, group = group, ...): group coerced to
## factor.
## # A tibble: 1 × 4
##     df1   df2 statistic      p
##   <int> <int>     <dbl>  <dbl>
## 1     1    22      3.58 0.0716
# Save output
EQUAL.VAR <- dat.clean %>% levene_test(FORMULA) %>% pull(p) > 0.05
## Warning in leveneTest.default(y = y, group = group, ...): group coerced to
## factor.

No outliers were identified. Data is normally distributed and has equal variance. Hence we use t-test.

6.1.3.2.2 PERFORM TEST

T-test
We are now ready to perform the test

stat.test <- dat.clean %>% 
  t_test(FORMULA,
         var.equal = EQUAL.VAR,
         detailed = TRUE,
         paired = FALSE,
         alternative = "two.sided") %>%
  add_significance()
stat.test
## # A tibble: 1 × 16
##   estimate estimate1 estimate2 .y.   group1 group2    n1    n2 statistic       p
##      <dbl>     <dbl>     <dbl> <chr> <chr>  <chr>  <int> <int>     <dbl>   <dbl>
## 1    0.603      3.20      2.60 pfos… d08    d21       12    12      3.96 6.64e-4
## # ℹ 6 more variables: df <dbl>, conf.low <dbl>, conf.high <dbl>, method <chr>,
## #   alternative <chr>, p.signif <chr>

Effect size
The effect size is calculated as Cohen’s D

dat.clean %>% cohens_d(FORMULA, 
                       var.equal = EQUAL.VAR,
                       paired = FALSE)
## # A tibble: 1 × 7
##   .y.           group1 group2 effsize    n1    n2 magnitude
## * <chr>         <chr>  <chr>    <dbl> <int> <int> <ord>    
## 1 pfos_liver_mg d08    d21       1.62    12    12 large
6.1.3.2.3 Nested analysis and Figure
# Set variables for inner and outer analyses
INNER.VAR <- "feed"
OUTER.VAR <- "dissection"

# Statistics costumed for facet plotting
## Pairwise comparison for inner variable
stat.in <- dat.clean %>%
  group_by(dissection) %>%
  t_test(pfos_liver_mg ~ feed, 
         paired = FALSE, var.equal = EQUAL.VAR, 
         detailed = TRUE, alternative = "two.sided") %>%
  add_significance() %>%
  p_format("p", accuracy = 0.001, trailing.zero = TRUE, new.col = TRUE)
stat.in
## # A tibble: 2 × 18
##   dissection estimate estimate1 estimate2 .y.          group1 group2    n1    n2
## * <chr>         <dbl>     <dbl>     <dbl> <chr>        <chr>  <chr>  <int> <int>
## 1 d08          0.105       3.25      3.15 pfos_liver_… HF     LF         6     6
## 2 d21          0.0371      2.62      2.58 pfos_liver_… HF     LF         6     6
## # ℹ 9 more variables: statistic <dbl>, p <dbl>, df <dbl>, conf.low <dbl>,
## #   conf.high <dbl>, method <chr>, alternative <chr>, p.signif <chr>,
## #   p.format <chr>
## Pairwise comparison for outer variable
stat.out <- dat.clean %>%
  t_test(pfos_liver_mg ~ dissection,
         paired = FALSE, var.equal = EQUAL.VAR,
         detailed = TRUE, alternative = "two.sided") %>%
  add_significance() %>%
  p_format("p", accuracy = 0.001, trailing.zero = TRUE, new.col = TRUE)
stat.out
## # A tibble: 1 × 17
##   estimate estimate1 estimate2 .y.   group1 group2    n1    n2 statistic       p
## *    <dbl>     <dbl>     <dbl> <chr> <chr>  <chr>  <int> <int>     <dbl>   <dbl>
## 1    0.603      3.20      2.60 pfos… d08    d21       12    12      3.96 6.64e-4
## # ℹ 7 more variables: df <dbl>, conf.low <dbl>, conf.high <dbl>, method <chr>,
## #   alternative <chr>, p.signif <chr>, p.format <chr>
## Calculate positions statistics on plot
stat.in <- stat.in %>% add_xy_position(x = "dissection", dodge = 0.8)
stat.out <- stat.out %>% add_xy_position(x = "dissection")
stat.out$y.position <- max(stat.in$y.position)*1.1

# Calculate slope and intercept for each group
reg_lines <- dat.clean %>%
  group_by(feed) %>%
  summarize(slope = coef(lm(pfos_liver_mg ~ dissection))[2],
            intercept = coef(lm(pfos_liver_mg ~ dissection))[1])

# Create ggboxplot with regression lines for feed types and slope values as text
p <- ggboxplot(dat.clean, x = "dissection", y = "pfos_liver_mg",
               fill = "feed",
               color = "feed",
               add = "jitter",
               add.params = list(size = 1)) +
  theme_pubr(legend = "top") +
  scale_fill_manual(values = params$COLFEED, labels = c("HF","LF"), name = "Feed") +
  scale_color_manual(breaks = dat.clean$feed, values = c("LF" = "#222222","HF" = "#222222", "HF_PFOS" = "#004400","LF_PFOS" = "#000066")) +
  scale_x_discrete(name = "Day", labels = c("Day 8","Day 21")) +
  guides(color = FALSE) +
  theme(axis.title.x=element_blank()) +
  geom_smooth(aes(group = feed, color = feedtreat), method = "lm", se = FALSE, size = 0.5, fullrange = TRUE, linetype = "dashed")

# Add statistics
p.stat <- p + 
  stat_pvalue_manual(stat.in, label = "p.signif", tip.length = 0, hide.ns = TRUE, color = "red") +
  stat_pvalue_manual(stat.out, label = "p.signif", tip.length = 0, hide.ns = TRUE, y.position = 4.1) +
  scale_y_continuous(name ="Liver PFOS mg", expand = expansion(mult = c(0.01, 0.1)), limits = c(2,4.3), breaks = seq(2,4.2,0.5)) +
  annotate("text", x = 2, y = 3.75, size = 3, label = paste0("HF: ",round(reg_lines$slope[1],2)," mg/day")) +
  annotate("text", x = 2, y = 3.6, size = 3, label = paste0("LF: ",round(reg_lines$slope[2],2)," mg/day"))

# View the final plot
p.stat
## `geom_smooth()` using formula = 'y ~ x'

# Save plot as Rdata element
p.livermg <- p.stat
save(p.livermg, file = "plots/animal_data/pfos/systemic_livermg_figure.Rdata")

# Save plot as PNG and PDF formats
suppressMessages(ggsave(filename = paste0("plots/animal_data/pfos/systemic_liver_",OUTCOME,".png"), plot = p.stat, device = "png", units = "mm", dpi = 300, height = 100, width = 90))
suppressMessages(ggsave(filename = paste0("plots/animal_data/pfos/systemic_liver_",OUTCOME,".pdf"), plot = p.stat, device = "pdf", units = "mm", dpi = 300, height = 100, width = 90))

# clear the environment and release memory
rm(list = ls(all.names = TRUE)[ls(all.names = TRUE) != "params"])
invisible(gc())

6.1.4 Liver PFOS Accumulation

In this section we explore the large difference in HF-PFOS and LF-PFOS on Day 8 measured in ug/g. The hypothesis is that the increased relative liver weight in LF-PFOS in Day 8 plays a role in lowering the PFOS concentration per gram liver as daily PFOS dosing is dependent on body weight. In the available dataset there are already values available for liver weight at day of dissection (liver_wt), relative liver weight at day of dissection (liver_wtbw), total dose of PFOS given per rat dependent on body weight in mg (pfos_total_mg), and PFOS concentration in liver samples (per gram liver: pfos_liver_ugg, and for total liver weight: pfos_liver_mg)

6.1.4.1 mg

6.1.4.1.1 Prepare data and create figure
# Load data
load("R_objects/animal_data.Rdata")

dat.clean <- dat %>% subset(treatment == "PFOS") %>%
  select("rat_name","dissection","treatment","feed","feedtreat","bw_8","bw_21","liver_wt","liver_wtbw","pfos_total_mg","pfos_liver_ugg","pfos_liver_mg")


for (rat in dat.clean$rat_name) {
  dat.clean$pfos_liver_pct <- (dat.clean$pfos_liver_mg / dat.clean$pfos_total_mg)*100
}

# Set names of variables
PREDICTOR <- "dissection"#c("feed","day")
OUTCOME <- "pfos_liver_pct"
SUBJECT <- "rat_name"

# Will you run a paired test? (set variable to `TRUE` or `FALSE`)
PAIRED <- FALSE

# Create formula
FORMULA <- as.formula(paste(OUTCOME, PREDICTOR, sep = "~"))

# Sort data for paired test
if (PAIRED) {
  # Order data
  dat.clean <- arrange(dat.clean, !!sym(SUBJECT))
  
  # Remove unpaired samples
  dat.clean <- dat.clean %>% 
    group_by(!!sym(SUBJECT)) %>%
    filter(n() != 1) %>%
    arrange(!!sym(PREDICTOR), !!sym(SUBJECT)) %>%
    droplevels() %>% 
    ungroup()
}
# identify outliers
dat.clean %>%
  group_by(!!sym(PREDICTOR)) %>%
  identify_outliers(!!sym(OUTCOME))
##  [1] dissection     rat_name       treatment      feed           feedtreat     
##  [6] bw_8           bw_21          liver_wt       liver_wtbw     pfos_total_mg 
## [11] pfos_liver_ugg pfos_liver_mg  pfos_liver_pct is.outlier     is.extreme    
## <0 rækker> (eller 0-længde row.names)
# Run Shapiro test (normality)
dat.clean %>% 
  group_by(!!sym(PREDICTOR)) %>%
  shapiro_test(!!sym(OUTCOME))
## # A tibble: 2 × 4
##   dissection variable       statistic     p
##   <chr>      <chr>              <dbl> <dbl>
## 1 d08        pfos_liver_pct     0.962 0.815
## 2 d21        pfos_liver_pct     0.976 0.961
# Create QQplot
ggqqplot(dat.clean, x = OUTCOME, facet.by = PREDICTOR)

# Run test for equal variance
dat.clean %>% levene_test(FORMULA)
## Warning in leveneTest.default(y = y, group = group, ...): group coerced to
## factor.
## # A tibble: 1 × 4
##     df1   df2 statistic     p
##   <int> <int>     <dbl> <dbl>
## 1     1    22      1.03 0.320
# Save output
EQUAL.VAR <- dat.clean %>% levene_test(FORMULA) %>% pull(p) > 0.05
## Warning in leveneTest.default(y = y, group = group, ...): group coerced to
## factor.
# Set variables for inner and outer analyses
INNER.VAR <- "feed"
OUTER.VAR <- "dissection"


# Statistics costumed for facet plotting
## Pairwise comparison for inner variable
stat.in <- dat.clean %>%
  group_by(dissection) %>%
  t_test(pfos_liver_pct ~ feed, 
         paired = FALSE, var.equal = EQUAL.VAR, 
         detailed = TRUE, alternative = "two.sided") %>%
  add_significance() %>%
  p_format("p", accuracy = 0.001, trailing.zero = TRUE, new.col = TRUE)
stat.in
## # A tibble: 2 × 18
##   dissection estimate estimate1 estimate2 .y.          group1 group2    n1    n2
## * <chr>         <dbl>     <dbl>     <dbl> <chr>        <chr>  <chr>  <int> <int>
## 1 d08           2.76       48.8      46.0 pfos_liver_… HF     LF         6     6
## 2 d21           0.182      40.0      39.8 pfos_liver_… HF     LF         6     6
## # ℹ 9 more variables: statistic <dbl>, p <dbl>, df <dbl>, conf.low <dbl>,
## #   conf.high <dbl>, method <chr>, alternative <chr>, p.signif <chr>,
## #   p.format <chr>
## Pairwise comparison for outer variable
stat.out <- dat.clean %>%
  t_test(pfos_liver_pct ~ dissection,
         paired = FALSE, var.equal = EQUAL.VAR,
         detailed = TRUE, alternative = "two.sided") %>%
  add_significance() %>%
  p_format("p", accuracy = 0.001, trailing.zero = TRUE, new.col = TRUE)
stat.out
## # A tibble: 1 × 17
##   estimate estimate1 estimate2 .y.   group1 group2    n1    n2 statistic       p
## *    <dbl>     <dbl>     <dbl> <chr> <chr>  <chr>  <int> <int>     <dbl>   <dbl>
## 1     7.55      47.4      39.9 pfos… d08    d21       12    12      5.32 2.46e-5
## # ℹ 7 more variables: df <dbl>, conf.low <dbl>, conf.high <dbl>, method <chr>,
## #   alternative <chr>, p.signif <chr>, p.format <chr>
## Calculate positions statistics on plot
stat.in <- stat.in %>% add_xy_position(x = "dissection", dodge = 0.8)
stat.out <- stat.out %>% add_xy_position(x = "dissection")
stat.out$y.position <- max(stat.in$y.position)*1.1

# Calculate slope and intercept for each group
reg_lines <- dat.clean %>%
  group_by(feed) %>%
  summarize(slope = coef(lm(pfos_liver_pct ~ dissection))[2],
            intercept = coef(lm(pfos_liver_pct ~ dissection))[1])

# Create ggboxplot with regression lines for feed types and slope values as text
p <- ggboxplot(dat.clean, x = "dissection", y = "pfos_liver_pct",
               fill = "feed",
               color = "feed",
               add = "jitter",
               add.params = list(size = 1)) +
  theme_pubr(legend = "top") +
  scale_fill_manual(values = params$COLFEED, labels = c("HF","LF"), name = "Feed") +
  scale_color_manual(breaks = dat.clean$feed, values = c("LF" = "#222222","HF" = "#222222", "HF_PFOS" = "#004400","LF_PFOS" = "#000066")) +
  scale_x_discrete(name = "Day", labels = c("Day 8","Day 21")) +
  guides(color = FALSE) +
  theme(axis.title.x=element_blank()) +
  geom_smooth(aes(group = feed, color = feedtreat), method = "lm", se = FALSE, size = 0.5, fullrange = TRUE, linetype = "dashed")

# Add statistics
p.pct <- p + 
  stat_pvalue_manual(stat.in, label = "p.signif", tip.length = 0, hide.ns = TRUE, color = "red") +
  stat_pvalue_manual(stat.out, label = "p.signif", tip.length = 0, hide.ns = TRUE, y.position = 55) +
  scale_y_continuous(name ="Total dosed PFOS in Liver (%)", expand = expansion(mult = c(0.01, 0.05)), labels = function(x) paste0(x, "%")) +
  annotate("text", x = 2, y = 51, size = 3, label = paste0("HF: ",round(reg_lines$slope[1],2)," %/day")) +
  annotate("text", x = 2, y = 50, size = 3, label = paste0("LF: ",round(reg_lines$slope[2],2)," %/day"))

p.pct
## `geom_smooth()` using formula = 'y ~ x'

# Save plot as PNG and PDF formats
suppressMessages(ggsave(filename = paste0("plots/animal_data/pfos/systemic_liver_",OUTCOME,".png"), plot = p.pct, device = "png", units = "mm", dpi = 300, height = 100, width = 90))
suppressMessages(ggsave(filename = paste0("plots/animal_data/pfos/systemic_liver_",OUTCOME,".pdf"), plot = p.pct, device = "pdf", units = "mm", dpi = 300, height = 100, width = 90))

#test liver_wtbw between HF-PFOS and LF-PFOS
hf <- dat.clean %>% subset(feed == "HF" & dissection == "d08")
lf <- dat.clean %>% subset(feed == "LF" & dissection == "d08")

lfhf_liver <- (mean(lf$liver_wtbw) / mean(hf$liver_wtbw))
print(paste0("Ratio between relative LF and HF liver weight: ",round(lfhf_liver,3)))
## [1] "Ratio between relative LF and HF liver weight: 1.052"
lfhf_pfos <- (mean(lf$pfos_liver_ugg) / mean(hf$pfos_liver_ugg))
print(paste0("Ratio between LF and HF liver PFOS ug/g: ",round(lfhf_pfos,3)))
## [1] "Ratio between LF and HF liver PFOS ug/g: 0.875"
# Setup and save Liver PFOS plots for final figure
load("plots/animal_data/pfos/systemic_liverugg_figure.Rdata")
load("plots/animal_data/pfos/systemic_livermg_figure.Rdata")

p.liver <- p.liver + theme(legend.position = "none")
p.pct <- p.pct + theme(legend.position = "none")

p1 <- ggarrange(p.liver,p.livermg,
                nrow = 1, ncol = 2,
                widths = c(1,1),
                labels = c("B","C"))
## `geom_smooth()` using formula = 'y ~ x'
## `geom_smooth()` using formula = 'y ~ x'
p1

suppressMessages(ggsave(filename = paste0("plots/animal_data/pfos/systemic_liver_combined.png"), plot = p1, device = "png", units = "mm", dpi = 300, height = 100, width = 150))
suppressMessages(ggsave(filename = paste0("plots/animal_data/pfos/systemic_liver_combined.pdf"), plot = p1, device = "pdf", units = "mm", dpi = 300, height = 100, width = 150))

# Save plot as Rdata element
p.liverpct <- p.pct + theme(legend.position = "none")
p.livercomb <- p1 + theme(legend.position = "none")
save(p.livercomb, p.liverpct, file = "plots/animal_data/pfos/systemic_liver_figure.Rdata")
6.1.4.1.2 Conclusion

No difference in total accumulated PFOS relative to total amount dosed was observed meaning that the observed difference in µg/g arise from difference in liver size relative to body size - this also reflects that the liver has an active uptake of PFOS which might me in equilibrium with blood concentration.

6.2 EXCRETED MEASUREMENTS

Following PFOS measurements from excreted samples are analysed and presented here:

  • Feces from day 8, 12, 16, and 21
  • Cecum from dissection days 8 and 21
  • Urine from day 8, 16, and 21

6.2.1 Feces

6.2.1.1 ug/g

#####Prepare data

# Load data
load("R_objects/animal_data.Rdata")
params <- readRDS("R_objects/animal_params.RDS")

# Set parameters
PREDICTOR <- c("feed","day")
OUTCOME <- "conc"
SUBJECT <- "rat_name"

# Create data frame for data representation
dat.clean <- dat %>% select(rat_name, feed, treatment, feedtreat, dissection, bw_8, bw_12, bw_16, bw_21, pfos_total_mg, pfos_feces8_ugg, pfos_feces12_ugg, pfos_feces16_ugg, pfos_feces21_ugg) %>%
  pivot_longer(., cols = c(pfos_feces8_ugg, pfos_feces12_ugg, pfos_feces16_ugg, pfos_feces21_ugg), names_to = "pfos_day", values_to = "conc")

# Create column for day of sampling
dat.clean <- dat.clean %>% 
  mutate("day" = case_when(pfos_day == "pfos_feces8_ugg" ~ "d08",
                           pfos_day == "pfos_feces12_ugg" ~ "d12",
                           pfos_day == "pfos_feces16_ugg" ~ "d16",
                           pfos_day == "pfos_feces21_ugg" ~ "d21"))
  
# Order dataframe for analysis
dat.clean <- dat.clean[order(dat.clean$day),]

# Remove rows with NA
dat.clean <- subset(dat.clean, !is.na(conc))
# Subset to only PFOS groups
dat.clean <- subset(dat.clean, dat.clean$treatment == "PFOS")
dat.clean
## # A tibble: 60 × 13
##    rat_name feed  treatment feedtreat dissection  bw_8 bw_12 bw_16 bw_21
##    <chr>    <chr> <chr>     <chr>     <chr>      <int> <dbl> <dbl> <dbl>
##  1 R13      LF    PFOS      LF_PFOS   d08          373   NA    NA    NA 
##  2 R14      LF    PFOS      LF_PFOS   d08          390   NA    NA    NA 
##  3 R15      LF    PFOS      LF_PFOS   d08          341   NA    NA    NA 
##  4 R16      LF    PFOS      LF_PFOS   d08          299   NA    NA    NA 
##  5 R17      LF    PFOS      LF_PFOS   d08          322   NA    NA    NA 
##  6 R18      LF    PFOS      LF_PFOS   d08          423   NA    NA    NA 
##  7 R19      LF    PFOS      LF_PFOS   d21          344  361.  397.  403.
##  8 R20      LF    PFOS      LF_PFOS   d21          357  383.  421.  442.
##  9 R21      LF    PFOS      LF_PFOS   d21          342  366.  391.  422.
## 10 R22      LF    PFOS      LF_PFOS   d21          349  382.  417.  432.
## # ℹ 50 more rows
## # ℹ 4 more variables: pfos_total_mg <dbl>, pfos_day <chr>, conc <dbl>,
## #   day <chr>
# Create formula
PREDICTOR.F <- ifelse(length(PREDICTOR) > 1, paste(PREDICTOR, collapse = "*"), PREDICTOR)
FORMULA <- as.formula(paste(OUTCOME,PREDICTOR.F, sep = " ~ "))

# Summary samples in groups
dat.clean %>% group_by(across(all_of(PREDICTOR))) %>% get_summary_stats(!!sym(OUTCOME), type = "full") %>% select("feed","day","n","min","max","mean","sd","se")
## # A tibble: 8 × 8
##   feed  day       n   min   max  mean    sd    se
##   <chr> <chr> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
## 1 HF    d08      12 2.21   4.65  3.49 0.777 0.224
## 2 HF    d12       6 1.89   3.06  2.49 0.393 0.16 
## 3 HF    d16       6 1.63   2.68  2.22 0.426 0.174
## 4 HF    d21       6 1.32   2.75  2.06 0.612 0.25 
## 5 LF    d08      12 1.09   2.18  1.61 0.292 0.084
## 6 LF    d12       6 0.864  1.74  1.34 0.349 0.142
## 7 LF    d16       6 1.10   1.86  1.42 0.322 0.132
## 8 LF    d21       6 0.877  1.23  1.06 0.129 0.053
# Create plot
bxp <- dat.clean %>%
  ggboxplot(x = if_else(length(PREDICTOR) > 1, PREDICTOR[2],PREDICTOR[1]),
            y = OUTCOME,
            color = PREDICTOR[1],
            facet.by = if(length(PREDICTOR) == 3) PREDICTOR[3],
            palette = "jco")
bxp

# Test for outliers
dat.clean %>% 
  group_by(across(all_of(PREDICTOR))) %>% 
  identify_outliers(!!sym(OUTCOME))
##  [1] feed          day           rat_name      treatment     feedtreat    
##  [6] dissection    bw_8          bw_12         bw_16         bw_21        
## [11] pfos_total_mg pfos_day      conc          is.outlier    is.extreme   
## <0 rækker> (eller 0-længde row.names)
# Build the linear model
model  <- lm(FORMULA, data = dat.clean)

# Create a QQ plot of residuals
ggqqplot(residuals(model))

# Compute Shapiro-Wilk test of normality
shapiro_test(residuals(model))
## # A tibble: 1 × 3
##   variable         statistic p.value
##   <chr>                <dbl>   <dbl>
## 1 residuals(model)     0.980   0.422
plot(model, 1)

# Run levene's test
dat.clean %>% levene_test(FORMULA)
## # A tibble: 1 × 4
##     df1   df2 statistic      p
##   <int> <int>     <dbl>  <dbl>
## 1     7    52      2.74 0.0170
# Save result
EQUAL.VAR <- dat.clean %>% levene_test(FORMULA) %>% pull(p) > 0.05

This all shows that normalised cecum weight data has two outliers, is normally distribution and has equal variance. Therefore we use a Welch ANOVA test with Games Howell posthoc test to test data for all factors of interest, while using unpaired t-test for pairwise comparisons.

6.2.1.1.1 WELCH’s ANOVA One-Way test

Perform test

If we had equality of variance we can now run a one-way ANOVA tests anova_test() (if we have equal variance) or a welch_anova_test() (if variance vary).

if(EQUAL.VAR) {
  res.aov <- dat.clean %>% anova_test(FORMULA)
  res.aov
} else {
  res.aov <- dat.clean %>% welch_anova_test(FORMULA)
  res.aov
}
## # A tibble: 1 × 7
##   .y.       n statistic   DFn   DFd            p method     
## * <chr> <int>     <dbl> <dbl> <dbl>        <dbl> <chr>      
## 1 conc     60      24.8     7  19.3 0.0000000236 Welch ANOVA

ANOVA shows that day and feed have significant effects on the overall data.

Perform posthoc test

A significant one-way ANOVA is generally followed up by Tukey post-hoc tests to perform multiple pairwise comparisons between groups. When running relaxed Welch one-way test, the Games-Howell post hoc test or pairwise t-tests (with no assumption of equal variances) can be used to compare all possible combinations of group differences.

if(EQUAL.VAR) {
  pwc <- dat.clean %>% tukey_hsd(FORMULA)
  pwc
} else {
  PREDICTOR2 <- "day"
  FORMULA <- as.formula(paste(OUTCOME,PREDICTOR2, sep = " ~ "))
  pwc <- dat.clean %>% games_howell_test(FORMULA)
  pwc
}
## # A tibble: 6 × 8
##   .y.   group1 group2 estimate conf.low conf.high p.adj p.adj.signif
## * <chr> <chr>  <chr>     <dbl>    <dbl>     <dbl> <dbl> <chr>       
## 1 conc  d08    d12     -0.632    -1.46     0.191  0.181 ns          
## 2 conc  d08    d16     -0.728    -1.48     0.0246 0.061 ns          
## 3 conc  d08    d21     -0.987    -1.80    -0.176  0.012 *           
## 4 conc  d12    d16     -0.0957   -0.811    0.620  0.982 ns          
## 5 conc  d12    d21     -0.355    -1.13     0.421  0.59  ns          
## 6 conc  d16    d21     -0.259    -0.960    0.441  0.734 ns

Posthoc analysis however show significant differences between to be between overall treatment, overall dissection days, as well as cross-day comparisons.

6.2.1.1.2 Nested analysis and Figure
# Set variables for inner and outer analyses
INNER.VAR <- "feed"
OUTER.VAR <- "day"

# Statistics costumed for facet plotting
## Pairwise comparison for inner variable
stat.in <- dat.clean %>%
  group_by(day) %>%
  t_test(conc ~ feed, 
         paired = FALSE, var.equal = EQUAL.VAR, 
         detailed = TRUE, alternative = "two.sided") %>%
  add_significance() %>%
  p_format("p", accuracy = 0.001, trailing.zero = TRUE, new.col = TRUE)
stat.in
## # A tibble: 4 × 18
##   day   estimate estimate1 estimate2 .y.   group1 group2    n1    n2 statistic
## * <chr>    <dbl>     <dbl>     <dbl> <chr> <chr>  <chr>  <int> <int>     <dbl>
## 1 d08      1.88       3.49      1.61 conc  HF     LF        12    12      7.84
## 2 d12      1.15       2.49      1.34 conc  HF     LF         6     6      5.34
## 3 d16      0.806      2.23      1.42 conc  HF     LF         6     6      3.69
## 4 d21      1.00       2.07      1.06 conc  HF     LF         6     6      3.93
## # ℹ 8 more variables: p <dbl>, df <dbl>, conf.low <dbl>, conf.high <dbl>,
## #   method <chr>, alternative <chr>, p.signif <chr>, p.format <chr>
## Pairwise comparison for outer variable
stat.out <- dat.clean %>%
  welch_anova_test(conc ~ day) %>%
  # dunn_test(conc ~ day, p.adjust.method = "fdr") %>%
  add_significance() %>%
  p_format("p", accuracy = 0.001, trailing.zero = TRUE, new.col = TRUE)
stat.out
## # A tibble: 1 × 9
##   .y.       n statistic   DFn   DFd     p method      p.signif p.format
## * <chr> <int>     <dbl> <dbl> <dbl> <dbl> <chr>       <chr>    <chr>   
## 1 conc     60      3.62     3  28.8 0.025 Welch ANOVA *        0.025
pwc2 <- dat.clean %>%
  # kruskal_test(conc ~ day) %>%
  games_howell_test(conc ~ day) %>%
  add_significance() %>%
  p_format("p.adj", accuracy = 0.001, trailing.zero = TRUE, new.col = TRUE)
pwc2
## # A tibble: 6 × 9
##   .y.   group1 group2 estimate conf.low conf.high p.adj p.adj.signif
## * <chr> <chr>  <chr>     <dbl>    <dbl>     <dbl> <dbl> <chr>       
## 1 conc  d08    d12     -0.632    -1.46     0.191  0.181 ns          
## 2 conc  d08    d16     -0.728    -1.48     0.0246 0.061 ns          
## 3 conc  d08    d21     -0.987    -1.80    -0.176  0.012 *           
## 4 conc  d12    d16     -0.0957   -0.811    0.620  0.982 ns          
## 5 conc  d12    d21     -0.355    -1.13     0.421  0.59  ns          
## 6 conc  d16    d21     -0.259    -0.960    0.441  0.734 ns          
## # ℹ 1 more variable: p.adj.format <chr>
## Calculate positions statistics on plot
stat.in <- stat.in %>% add_xy_position(x = "day", dodge = 0.8)
pwc2 <- pwc2 %>% add_xy_position(x = "day")
pwc2$y.position <- max(stat.in$y.position)*1.1

# Calculate slope and intercept for each group
reg_lines <- dat.clean %>%
  group_by(feed) %>%
  summarize(slope = coef(lm(conc ~ day))[2],
            intercept = coef(lm(conc ~ day))[1])

# Statistical test of regression slopes from all d21 rats
rls <- dat.clean %>% subset(dissection == "d21") %>%
  group_by(rat_name) %>%
  summarize(slope = coef(lm(conc ~ day))[2],
            intercept = coef(lm(conc ~ day))[1])
rls$group <- rep(c("LF","HF"), each = 6)

stat.rls <- rls %>%
  t_test(slope ~ group,
         paired = FALSE, var.equal = EQUAL.VAR, 
         detailed = TRUE, alternative = "two.sided") %>%
  add_significance() %>%
  p_format("p", accuracy = 0.001, trailing.zero = TRUE, new.col = TRUE)
stat.rls
## # A tibble: 1 × 17
##   estimate estimate1 estimate2 .y.   group1 group2    n1    n2 statistic     p
## *    <dbl>     <dbl>     <dbl> <chr> <chr>  <chr>  <int> <int>     <dbl> <dbl>
## 1   -0.561    -0.770    -0.209 slope HF     LF         6     6     -1.68 0.126
## # ℹ 7 more variables: df <dbl>, conf.low <dbl>, conf.high <dbl>, method <chr>,
## #   alternative <chr>, p.signif <chr>, p.format <chr>
# Create ggboxplot with regression lines for feed types and slope values as text
p <- ggboxplot(dat.clean, x = "day", y = "conc",
               fill = "feed",
               color = "feed",
               add = "jitter",
               add.params = list(size = 1)) +
  theme_pubr(legend = "top") +
  scale_fill_manual(values = params$COLFEED, name = "Feed") +
  scale_color_manual(breaks = dat.clean$feed, values = c("LF" = "#222222","HF" = "#222222")) +
  scale_x_discrete(name = "Day", labels = c("Day 8","Day 12","Day 16","Day 21")) +
  guides(color = FALSE) +
  theme(axis.title.x=element_blank())

# Add statistics
p.stat <- p + 
  stat_pvalue_manual(stat.in, label = "p.signif", tip.length = 0, hide.ns = TRUE, color = "red") +
  stat_pvalue_manual(pwc2, label = "p.adj.signif", tip.length = 0, hide.ns = TRUE) +#, y.position = c(5.4,5.7,6,4.8,5.1,4.5))+#, limits = c(30,80),breaks = seq(30,80,10)) +
  scale_y_continuous(name ="Faeces PFOS ug / g", expand = expansion(mult = c(0.01, 0.1)))
# View the final plot
p.stat

# Save plot as Rdata element
p.faeces <- p.stat
save(p.faeces, file = "plots/animal_data/pfos/systemic_faeces_figure.Rdata")

# Save plots
suppressMessages(ggsave(filename = paste0("plots/animal_data/pfos/excreted_feces_",OUTCOME,".png"), plot = p.stat, device = "png", units = "mm", dpi = 300, height = 100, width = 150))
suppressMessages(ggsave(filename = paste0("plots/animal_data/pfos/excreted_feces_",OUTCOME,".pdf"), plot = p.stat, device = "pdf", units = "mm", dpi = 300, height = 100, width = 150))

# clear the environment and release memory
rm(list = ls(all.names = TRUE)[ls(all.names = TRUE) != "params"])
invisible(gc())

6.2.2 Cecum

6.2.2.1 ug/g

6.2.2.1.1 Prepare data

This section sets the variables to be used and prepares the data if necessary.

# load data 
load("R_objects/animal_data.Rdata")
params <- readRDS("R_objects/animal_params.RDS")

# Set names of variables
PREDICTOR <- "dissection"#c("feed","dissection")
OUTCOME <- "pfos_cecum_ugg"
SUBJECT <- "rat_name"

# Subset to a specific varible
dat.clean <- subset(dat, treatment == "PFOS")

# Remove rows with NA
dat.clean <- subset(dat.clean, !is.na(pfos_cecum_ugg))

# Will you run a paired test? (set variable to `TRUE` or `FALSE`)
PAIRED <- FALSE

# Create formula
FORMULA <- as.formula(paste(OUTCOME, PREDICTOR, sep = "~"))

# Summary samples in groups
dat.clean %>% group_by(across(all_of(c("feed","dissection")))) %>% get_summary_stats(!!sym(OUTCOME), type = "full") %>% select("feed","dissection","n","min","max","mean","sd","se")
## # A tibble: 4 × 8
##   feed  dissection     n   min   max  mean    sd    se
##   <chr> <chr>      <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
## 1 HF    d08            6 0.919 1.88  1.51  0.346 0.141
## 2 HF    d21            6 1.06  1.22  1.14  0.06  0.024
## 3 LF    d08            6 0.765 1.02  0.907 0.093 0.038
## 4 LF    d21            6 0.526 0.881 0.742 0.153 0.062
# Sort data for paired test
if (PAIRED) {
  # Order data
  dat.clean <- arrange(dat.clean, !!sym(SUBJECT))
  
  # Remove unpaired samples
  dat.clean <- dat.clean %>% 
    group_by(!!sym(SUBJECT)) %>%
    filter(n() != 1) %>%
    arrange(!!sym(PREDICTOR), !!sym(SUBJECT)) %>%
    droplevels() %>% 
    ungroup()
}

# identify outliers
dat.clean %>%
  group_by(!!sym(PREDICTOR)) %>%
  identify_outliers(!!sym(OUTCOME))
##  [1] dissection        rat_org           rat_name          cage             
##  [5] treatment         feed              feedtreat         feedtreatday     
##  [9] bw_minus18        bw_0              bw_1              bw_2             
## [13] bw_3              bw_4              bw_5              bw_6             
## [17] bw_7              bw_8              bw_12             bw_16            
## [21] bw_21             bw_gain08         bw_gain021        bw_gain821       
## [25] bloodvol_8        bloodvol_16       bloodvol_21       cecum_wt         
## [29] cecum_wtbw        cecum_norm        liver_wt          liver_wtbw       
## [33] liver_norm        brain_wt_estimate transit_0         transit_7        
## [37] transit_20        dose_total_ml     pfos_total_mg     pfos_liver_ugg   
## [41] pfos_liver_mg     pfos_liver8_pct   pfos_brain_ugg    pfos_brain_uggbw 
## [45] pfos_brain_ug     pfos_brain_pct    pfos_serum8_ugml  pfos_serum8_mg   
## [49] pfos_serum8_pct   pfos_serum16_ugml pfos_serum16_mg   pfos_serum16_pct 
## [53] pfos_serum21_ugml pfos_serum21_mg   pfos_serum21_pct  pfos_feces8_ugg  
## [57] pfos_feces12_ugg  pfos_feces16_ugg  pfos_feces21_ugg  pfos_cecum_ugg   
## [61] pfos_urine8_ugml  pfos_urine16_ugml pfos_urine21_ugml pH_je_up         
## [65] pH_je_down        pH_ileum          pH_cecum          acetic           
## [69] formic            propanoic         m2_propanoic      butanoic         
## [73] m3_butanoic       pentanoic         m4_pentanoic      hexanoic         
## [77] heptanoic         is.outlier        is.extreme       
## <0 rækker> (eller 0-længde row.names)
# Run Shapiro test
dat.clean %>% 
  group_by(!!sym(PREDICTOR)) %>%
  shapiro_test(!!sym(OUTCOME))
## # A tibble: 2 × 4
##   dissection variable       statistic      p
##   <chr>      <chr>              <dbl>  <dbl>
## 1 d08        pfos_cecum_ugg     0.865 0.0563
## 2 d21        pfos_cecum_ugg     0.915 0.250
# Create QQplot
ggqqplot(dat.clean, x = OUTCOME, facet.by = PREDICTOR)

# Run test
dat.clean %>% levene_test(FORMULA)
## Warning in leveneTest.default(y = y, group = group, ...): group coerced to
## factor.
## # A tibble: 1 × 4
##     df1   df2 statistic     p
##   <int> <int>     <dbl> <dbl>
## 1     1    22      1.63 0.214
# Save output
EQUAL.VAR <- dat.clean %>% levene_test(FORMULA) %>% pull(p) > 0.05
## Warning in leveneTest.default(y = y, group = group, ...): group coerced to
## factor.

No outliers were identified. Data is normally distributed and has equal variance. Hence we use t-test.

6.2.2.1.2 PERFORM TEST

T-test
We are now ready to perform the test

stat.test <- dat.clean %>% 
  t_test(FORMULA,
         var.equal = EQUAL.VAR,
         detailed = TRUE,
         paired = FALSE,
         alternative = "two.sided") %>%
  add_significance()
stat.test
## # A tibble: 1 × 16
##   estimate estimate1 estimate2 .y.    group1 group2    n1    n2 statistic      p
##      <dbl>     <dbl>     <dbl> <chr>  <chr>  <chr>  <int> <int>     <dbl>  <dbl>
## 1    0.268      1.21     0.941 pfos_… d08    d21       12    12      2.01 0.0569
## # ℹ 6 more variables: df <dbl>, conf.low <dbl>, conf.high <dbl>, method <chr>,
## #   alternative <chr>, p.signif <chr>

Effect size
The effect size is calculated as Cohen’s D

dat.clean %>% cohens_d(FORMULA, 
                       var.equal = EQUAL.VAR,
                       paired = FALSE)
## # A tibble: 1 × 7
##   .y.            group1 group2 effsize    n1    n2 magnitude
## * <chr>          <chr>  <chr>    <dbl> <int> <int> <ord>    
## 1 pfos_cecum_ugg d08    d21      0.821    12    12 large
6.2.2.1.3 Nested analysis and Figure
# Set variables for inner and outer analyses
INNER.VAR <- "feed"
OUTER.VAR <- "dissection"

# Statistics costumed for facet plotting
## Pairwise comparison for inner variable
stat.in <- dat.clean %>%
  group_by(dissection) %>%
  t_test(pfos_cecum_ugg ~ feed, 
         paired = FALSE, var.equal = EQUAL.VAR, 
         detailed = TRUE, alternative = "two.sided") %>%
  add_significance() %>%
  p_format("p", accuracy = 0.001, trailing.zero = TRUE, new.col = TRUE)
stat.in
## # A tibble: 2 × 18
##   dissection estimate estimate1 estimate2 .y.          group1 group2    n1    n2
## * <chr>         <dbl>     <dbl>     <dbl> <chr>        <chr>  <chr>  <int> <int>
## 1 d08           0.602      1.51     0.907 pfos_cecum_… HF     LF         6     6
## 2 d21           0.397      1.14     0.742 pfos_cecum_… HF     LF         6     6
## # ℹ 9 more variables: statistic <dbl>, p <dbl>, df <dbl>, conf.low <dbl>,
## #   conf.high <dbl>, method <chr>, alternative <chr>, p.signif <chr>,
## #   p.format <chr>
## Pairwise comparison for outer variable
stat.out <- dat.clean %>%
  t_test(pfos_cecum_ugg ~ dissection,
         paired = FALSE, var.equal = EQUAL.VAR,
         detailed = TRUE, alternative = "two.sided") %>%
  add_significance() %>%
  p_format("p", accuracy = 0.001, trailing.zero = TRUE, new.col = TRUE)
stat.out
## # A tibble: 1 × 17
##   estimate estimate1 estimate2 .y.    group1 group2    n1    n2 statistic      p
## *    <dbl>     <dbl>     <dbl> <chr>  <chr>  <chr>  <int> <int>     <dbl>  <dbl>
## 1    0.268      1.21     0.941 pfos_… d08    d21       12    12      2.01 0.0569
## # ℹ 7 more variables: df <dbl>, conf.low <dbl>, conf.high <dbl>, method <chr>,
## #   alternative <chr>, p.signif <chr>, p.format <chr>
## Calculate positions statistics on plot
stat.in <- stat.in %>% add_xy_position(x = "dissection", dodge = 0.8)
stat.out <- stat.out %>% add_xy_position(x = "dissection")
stat.out$y.position <- max(stat.in$y.position)*1.1

# Calculate slope and intercept for each group
reg_lines <- dat.clean %>%
  group_by(feed) %>%
  summarize(slope = coef(lm(pfos_cecum_ugg ~ dissection))[2],
            intercept = coef(lm(pfos_cecum_ugg ~ dissection))[1])

# Create ggboxplot with regression lines for feed types and slope values as text
p <- ggboxplot(dat.clean, x = "dissection", y = "pfos_cecum_ugg",
               fill = "feed",
               color = "feed",
               add = "jitter",
               add.params = list(size = 1)) +
  theme_pubr(legend = "top") +
  scale_fill_manual(values = params$COLFEED, name = "Feed") + #labels = c("High fibre","Low fibre")
  scale_color_manual(breaks = dat.clean$feed, values = c("LF" = "#222222","HF" = "#222222")) +
  scale_x_discrete(name = "Day", labels = c("Day 8","Day 21")) +
  guides(color = FALSE) +
  theme(axis.title.x=element_blank()) #+
  # geom_smooth(aes(group = feed, color = feed), method = "lm", se = FALSE, size = 0.5, fullrange = TRUE, linetype = "dashed")

# Add statistics
p.stat <- p + 
  stat_pvalue_manual(stat.in, label = "p.signif", tip.length = 0, hide.ns = TRUE, color = "red") +
  stat_pvalue_manual(stat.out, label = "p.signif", tip.length = 0, hide.ns = TRUE, y.position = 2.2) +
  scale_y_continuous(name ="Caecal content PFOS µg / g", expand = expansion(mult = c(0.01, 0.1)), limits = c(0.5,2.3), breaks = seq(0.5,2.3,0.5)) #+

# View the final plot
p.stat

# Save plot as Rdata element
p.caecum <- p.stat
save(p.caecum, file = "plots/animal_data/pfos/systemic_caecum_figure.Rdata")

# Save plot as PNG and PDF formats
suppressMessages(ggsave(filename = paste0("plots/animal_data/pfos/excreted_cecum_",OUTCOME,".png"), plot = p.stat, device = "png", units = "mm", dpi = 300, height = 100, width = 90))
suppressMessages(ggsave(filename = paste0("plots/animal_data/pfos/excreted_cecum_",OUTCOME,".pdf"), plot = p.stat, device = "pdf", units = "mm", dpi = 300, height = 100, width = 90))

# clear the environment and release memory
rm(list = ls(all.names = TRUE)[ls(all.names = TRUE) != "params"])
invisible(gc())

6.2.3 Urine

6.2.3.1 ug/mL

#####Prepare data

# Load data
load("R_objects/animal_data.Rdata")
params <- readRDS("R_objects/animal_params.RDS")

# Set parameters
PREDICTOR <- "day"#c("day","feed")
OUTCOME <- "conc"
SUBJECT <- "rat_name"

# Create data frame for data representation
dat.clean <- dat %>% select(rat_name, feed, treatment, feedtreat, dissection, bw_8, bw_21, pfos_total_mg, pfos_urine8_ugml, pfos_urine16_ugml, pfos_urine21_ugml) %>%
  pivot_longer(., cols = c(pfos_urine8_ugml, pfos_urine16_ugml, pfos_urine21_ugml), names_to = "pfos_day", values_to = "conc")

# Create column for day of sampling
dat.clean <- dat.clean %>% 
  mutate("day" = case_when(pfos_day == "pfos_urine8_ugml" ~ "d08",
                           pfos_day == "pfos_urine16_ugml" ~ "d16",
                           pfos_day == "pfos_urine21_ugml" ~ "d21"))
  
# Order dataframe for analysis
dat.clean <- dat.clean[order(dat.clean$day),]

# Remove rows with NA
dat.clean <- subset(dat.clean, !is.na(conc))
# Subset to only PFOS groups
dat.clean <- subset(dat.clean, dat.clean$treatment == "PFOS")
# Subset extreme outliers
dat.clean <- subset(dat.clean, !dat.clean$rat_name %in% c("R24","R46"))
dat.clean
## # A tibble: 41 × 11
##    rat_name feed  treatment feedtreat dissection  bw_8 bw_21 pfos_total_mg
##    <chr>    <chr> <chr>     <chr>     <chr>      <int> <dbl>         <dbl>
##  1 R13      LF    PFOS      LF_PFOS   d08          373   NA           7.12
##  2 R14      LF    PFOS      LF_PFOS   d08          390   NA           7.48
##  3 R15      LF    PFOS      LF_PFOS   d08          341   NA           6.46
##  4 R16      LF    PFOS      LF_PFOS   d08          299   NA           5.72
##  5 R17      LF    PFOS      LF_PFOS   d08          322   NA           6.22
##  6 R18      LF    PFOS      LF_PFOS   d08          423   NA           7.90
##  7 R20      LF    PFOS      LF_PFOS   d21          357  442.          6.74
##  8 R21      LF    PFOS      LF_PFOS   d21          342  422.          6.48
##  9 R22      LF    PFOS      LF_PFOS   d21          349  432.          6.80
## 10 R23      LF    PFOS      LF_PFOS   d21          337  442.          6.44
## # ℹ 31 more rows
## # ℹ 3 more variables: pfos_day <chr>, conc <dbl>, day <chr>
# Create formula
PREDICTOR.F <- ifelse(length(PREDICTOR) > 1, paste(PREDICTOR, collapse = "*"), PREDICTOR)
FORMULA <- as.formula(paste(OUTCOME,PREDICTOR.F, sep = " ~ "))

# Summary samples in groups
dat.clean %>% group_by(across(all_of(c("feed","day")))) %>% get_summary_stats(!!sym(OUTCOME), type = "full") %>% select("feed","day","n","min","max","mean","sd","se")
## # A tibble: 6 × 8
##   feed  day       n   min   max  mean    sd    se
##   <chr> <chr> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
## 1 HF    d08      11 0.33  1.19  0.58  0.234 0.07 
## 2 HF    d16       5 0.635 1.25  0.966 0.276 0.124
## 3 HF    d21       5 0.107 0.584 0.247 0.196 0.088
## 4 LF    d08      10 0.249 0.931 0.667 0.206 0.065
## 5 LF    d16       5 0.323 2.24  1.23  0.731 0.327
## 6 LF    d21       5 0.119 0.406 0.256 0.124 0.055
# Create plot
bxp <- dat.clean %>%
  ggboxplot(x = if_else(length(PREDICTOR) > 1, PREDICTOR[2],PREDICTOR[1]),
            y = OUTCOME,
            color = PREDICTOR[1],
            facet.by = if(length(PREDICTOR) == 3) PREDICTOR[3],
            palette = "jco")
bxp

# Test for outliers
dat.clean %>% 
  group_by(across(all_of(PREDICTOR))) %>% 
  identify_outliers(!!sym(OUTCOME))
## # A tibble: 1 × 13
##   day   rat_name feed  treatment feedtreat dissection  bw_8 bw_21 pfos_total_mg
##   <chr> <chr>    <chr> <chr>     <chr>     <chr>      <int> <dbl>         <dbl>
## 1 d16   R19      LF    PFOS      LF_PFOS   d21          344  403.          6.57
## # ℹ 4 more variables: pfos_day <chr>, conc <dbl>, is.outlier <lgl>,
## #   is.extreme <lgl>
# Build the linear model
model  <- lm(FORMULA, data = dat.clean)
# Create a QQ plot of residuals
ggqqplot(residuals(model))

# Compute Shapiro-Wilk test of normality
shapiro_test(residuals(model))
## # A tibble: 1 × 3
##   variable         statistic p.value
##   <chr>                <dbl>   <dbl>
## 1 residuals(model)     0.912 0.00393
plot(model, 1)

dat.clean %>% levene_test(FORMULA)
## Warning in leveneTest.default(y = y, group = group, ...): group coerced to
## factor.
## # A tibble: 1 × 4
##     df1   df2 statistic      p
##   <int> <int>     <dbl>  <dbl>
## 1     2    38      4.25 0.0215
# Save result
EQUAL.VAR <- dat.clean %>% levene_test(FORMULA) %>% pull(p) > 0.05
## Warning in leveneTest.default(y = y, group = group, ...): group coerced to
## factor.

Urine PFOS concentrations has five outliers, no normal distribution and has equal variance. This all shows that normalised cecum weight data has two outliers, is normally distribution and has equal variance. Therefore we use a non-parametric Kruskal-Wallis test with Dunn’s p-value adjustment.

6.2.3.1.1 Kruskal-Wallis test

Perform test

res.aov <- dat.clean %>% kruskal_test(FORMULA)
res.aov
## # A tibble: 1 × 6
##   .y.       n statistic    df         p method        
## * <chr> <int>     <dbl> <int>     <dbl> <chr>         
## 1 conc     41      21.8     2 0.0000189 Kruskal-Wallis

Effect size

The eta squared, based on the H-statistic, can be used as the measure of the Kruskal-Wallis test effect size. The interpretation values commonly in published literature are: 0.01- < 0.06 (small effect), 0.06 - < 0.14 (moderate effect) and >= 0.14 (large effect).

dat.clean %>% kruskal_effsize(FORMULA)
## # A tibble: 1 × 5
##   .y.       n effsize method  magnitude
## * <chr> <int>   <dbl> <chr>   <ord>    
## 1 conc     41   0.520 eta2[H] large

Post-hoc test if interaction is significant

A significant Kruskal-Wallis test is generally followed up by Dunn’s test to identify which groups are different. It’s also possible to use the Wilcoxon’s test to calculate pairwise comparisons between group levels with corrections for multiple testing.

# pairwise comparisons
pwc <- dat.clean %>% 
  dunn_test(FORMULA, p.adjust.method = "fdr") 
pwc
## # A tibble: 3 × 9
##   .y.   group1 group2    n1    n2 statistic          p     p.adj p.adj.signif
## * <chr> <chr>  <chr>  <int> <int>     <dbl>      <dbl>     <dbl> <chr>       
## 1 conc  d08    d16       21    10      2.11 0.0348     0.0348    *           
## 2 conc  d08    d21       21    10     -3.26 0.00113    0.00170   **          
## 3 conc  d16    d21       10    10     -4.61 0.00000402 0.0000120 ****

Posthoc analysis however show significant differences between to be between overall treatment, overall dissection days, as well as cross-day comparisons.

6.2.3.1.2 Nested analysis and Figure
# Set variables for inner and outer analyses
INNER.VAR <- "feed"
OUTER.VAR <- "day"

# Statistics costumed for facet plotting
## Pairwise comparison for inner variable
stat.in <- dat.clean %>%
  group_by(.data[[OUTER.VAR]]) %>%
  wilcox_test(as.formula(paste("conc ~", INNER.VAR, sep = " "))) %>%
  adjust_pvalue(method = "BH") %>%
  add_significance("p.adj") %>% 
  add_xy_position(x = OUTER.VAR, dodge = 0.8) %>%
  p_format("p.adj", accuracy = 0.0001, trailing.zero = TRUE, new.col = TRUE)
stat.in
## # A tibble: 3 × 16
##   day   .y.   group1 group2    n1    n2 statistic     p p.adj p.adj.signif
## * <chr> <chr> <chr>  <chr>  <int> <int>     <dbl> <dbl> <dbl> <chr>       
## 1 d08   conc  HF     LF        11    10        33 0.132 0.396 ns          
## 2 d16   conc  HF     LF         5     5        10 0.69  0.69  ns          
## 3 d21   conc  HF     LF         5     5         9 0.548 0.69  ns          
## # ℹ 6 more variables: y.position <dbl>, groups <named list>, x <dbl>,
## #   xmin <dbl>, xmax <dbl>, p.adj.format <chr>
## Pairwise comparison for outer variable
stat.out <- dat.clean %>%
  kruskal_test(conc ~ day) %>%
  add_significance() %>%
  p_format("p", accuracy = 0.001, trailing.zero = TRUE, new.col = TRUE)
stat.out
## # A tibble: 1 × 8
##   .y.       n statistic    df         p method         p.signif p.format
## * <chr> <int>     <dbl> <int>     <dbl> <chr>          <chr>    <chr>   
## 1 conc     41      21.8     2 0.0000189 Kruskal-Wallis ****     <0.001
pwc2 <- dat.clean %>%
  dunn_test(conc ~ day, p.adjust.method = "fdr") %>%
  add_significance() %>%
  p_format("p.adj", accuracy = 0.001, trailing.zero = TRUE, new.col = TRUE)
pwc2
## # A tibble: 3 × 10
##   .y.   group1 group2    n1    n2 statistic          p     p.adj p.adj.signif
## * <chr> <chr>  <chr>  <int> <int>     <dbl>      <dbl>     <dbl> <chr>       
## 1 conc  d08    d16       21    10      2.11 0.0348     0.0348    *           
## 2 conc  d08    d21       21    10     -3.26 0.00113    0.00170   **          
## 3 conc  d16    d21       10    10     -4.61 0.00000402 0.0000120 ****        
## # ℹ 1 more variable: p.adj.format <chr>
## Calculate positions statistics on plot
stat.in <- stat.in %>% add_xy_position(x = "day", dodge = 0.8)
pwc2 <- pwc2 %>% add_xy_position(x = "day")
pwc2$y.position <- max(stat.in$y.position)*1.1

# Create ggboxplot with regression lines for feed types and slope values as text
p <- ggboxplot(dat.clean, x = "day", y = "conc",
               fill = "feed",
               color = "feed",
               add = "jitter",
               add.params = list(size = 1)) +
  theme_pubr(legend = "top") +
  scale_fill_manual(values = params$COLFEED, name = "Feed") +
  scale_color_manual(breaks = dat.clean$feed, values = c("LF" = "#222222","HF" = "#222222")) +
  scale_x_discrete(name = "Day", labels = c("Day 8","Day 16","Day 21")) +
  guides(color = FALSE) +
  theme(axis.title.x=element_blank())

# Add statistics
p.stat <- p + 
  stat_pvalue_manual(stat.in, label = "p.adj.signif", tip.length = 0, hide.ns = TRUE, color = "red") +
  stat_pvalue_manual(pwc2, label = "p.adj.signif", tip.length = 0, hide.ns = TRUE, y.position = c(3.0,3.2,2.8), limits = c(0,3.2),breaks = seq(0,3.2,1)) +
  scale_y_continuous(name ="Urine PFOS ug / mL", expand = expansion(mult = c(0.01, 0.1)))
# View the final plot
p.stat

# Save plot as Rdata element
p.urine <- p.stat
save(p.urine, file = "plots/animal_data/pfos/systemic_urine_figure.Rdata")

suppressMessages(ggsave(filename = paste0("plots/animal_data/pfos/excreted_urine_",OUTCOME,".png"), plot = p.stat, device = "png", units = "mm", dpi = 300, height = 100, width = 120))
suppressMessages(ggsave(filename = paste0("plots/animal_data/pfos/excreted_urine_",OUTCOME,".pdf"), plot = p.stat, device = "pdf", units = "mm", dpi = 300, height = 100, width = 120))

# clear the environment and release memory
rm(list = ls(all.names = TRUE)[ls(all.names = TRUE) != "params"])
invisible(gc())

6.3 FIGURE 4 - PFOS concentrations

The following contains the code for creating the overall content for Figure 4 for the article. This PDF-output is further processed in vector graphics software to add ANOVA results to panel D.

6.3.1 Create figure

# Load plots
path <- "plots/animal_data/pfos/"
files <- as.list(dir(path = path, pattern = "*figure.Rdata"))
files <- lapply(files, function(x) paste0(path,x))

for (i in files) {
  load(i)
}

# Create combined plot

legend <- get_legend(p.blood)
## `geom_smooth()` using formula = 'y ~ x'
## Warning in get_plot_component(plot, "guide-box"): Multiple components found;
## returning the first one. To return all, use `return_all = TRUE`.
p1 <- ggarrange(p.brain, p.liver,p.livermg, p.blood,
               nrow = 1, ncol = 4,
               widths = c(2,2,2,3),
               align = "v",
               labels = c("A","B","C","D"),
               font.label = list(size = 24, face = "bold"),
               legend = FALSE,
               hjust = 0)
## `geom_smooth()` using formula = 'y ~ x'
## `geom_smooth()` using formula = 'y ~ x'
## `geom_smooth()` using formula = 'y ~ x'
## `geom_smooth()` using formula = 'y ~ x'
p1

p2 <- ggarrange(p.caecum, p.faeces, p.urine,
               nrow = 1, ncol = 3,
               widths = c(2,4,3),
               align = "v",
               labels = c("E","F","G"),
               font.label = list(size = 24, face = "bold"),
               legend = FALSE,
               hjust = 0)
p2

p3 <- ggarrange(legend,p1, p2,
                ncol = 1, nrow = 3,
                heights = c(1,8,8),
                align = "hv")
## Warning: Graphs cannot be vertically aligned unless the axis parameter is set.
## Placing graphs unaligned.
## Warning: Graphs cannot be horizontally aligned unless the axis parameter is
## set. Placing graphs unaligned.
p3

suppressMessages(ggsave(filename = paste0("plots/figures/Figure 4 - total_pfos_conc_figure_v5.png"), plot = p3, device = "png", units = "mm", dpi = 300, height = 180, width = 350))
suppressMessages(ggsave(filename = paste0("plots/figures/Figure 4 - total_pfos_conc_figure_v5.pdf"), plot = p3, device = "pdf", units = "mm", dpi = 300, height = 180, width = 350))

Final figure is polished in vector editing software.

7 PFOS ISOMER ANALYSIS

In this section we investigate branched (br-PFOS) and linear PFOS (l-PFOS) expressed as percentage of br-PFOS in the sample (br-PFOS %) based on calculated AMT. These data are obtained from quantitative mass spectrometry analysis on retention-time peaks for br-PFOS and l-PFOS, respectively.

Factors that will be investigated are each sample type (material: “Serum”, “Liver”, “Brain”, “Feces”, “Cecum”, “Urine”), day of sampling (differs between sample type but generally, day: “d08”, “d12”, “d16”, “d21”), feeding groups (feed: “LF”,“HF”) and level of br-PFOS % between samples and compound controls (“posctrl”).

7.1 Import data

Data is imported from CSV format. br-PFOS % is calculated by [br-PFOS] / [total PFOS] * 100.

# Load analysis data
dat <- read.csv(params$isomer_data, header = TRUE, sep = ";", dec = ",")

#Add additional metadata + calculate br_pct (br-PFOS percentage)
for (i in dat$id) {
  # Add columns for easier sorting of data downstream
  dat$dayfeed <- paste0(dat$day,"_",dat$feed)
  dat$matfeed <- paste0(dat$material,"_",dat$feed)
  # Calculate br-PFOS % from quantitative AMT data of br-PFOS and total PFOS
  dat$br_pct <- dat$pfos_b_amt / dat$pfos_t_amt
  dat$l_pct <- 1 - dat$br_pct
}

dat
##      id sample_name   rat_name sample_org    rat_org       type feed   material
## 1    13     R13Sd08        R13      R13S8        R13     sample   LF      Serum
## 2    14     R14Sd08        R14      R14S8        R14     sample   LF      Serum
## 3    15     R15Sd08        R15      R15S8        R15     sample   LF      Serum
## 4    16     R16Sd08        R16      R16S8        R16     sample   LF      Serum
## 5    17     R17Sd08        R17      R17S8        R17     sample   LF      Serum
## 6    18     R18Sd08        R18      R18S8        R18     sample   LF      Serum
## 7    19     R19Sd08        R19      R19S8        R19     sample   LF      Serum
## 8    20     R20Sd08        R20      R20S8        R20     sample   LF      Serum
## 9    21     R21Sd08        R21      R21S8        R21     sample   LF      Serum
## 10   22     R22Sd08        R22      R22S8        R22     sample   LF      Serum
## 11   23     R23Sd08        R23      R23S8        R23     sample   LF      Serum
## 12   24     R24Sd08        R24      R24S8        R24     sample   LF      Serum
## 13   37     R37Sd08        R37      R37S8        R37     sample   HF      Serum
## 14   38     R38Sd08        R38      R38S8        R38     sample   HF      Serum
## 15   39     R39Sd08        R39      R39S8        R39     sample   HF      Serum
## 16   40     R40Sd08        R40      R40S8        R40     sample   HF      Serum
## 17   41     R43Sd08        R43      R41S8        R41     sample   HF      Serum
## 18   42     R44Sd08        R44      R42S8        R42     sample   HF      Serum
## 19   43     R41Sd08        R41      R43S8        R43     sample   HF      Serum
## 20   44     R42Sd08        R42      R44S8        R44     sample   HF      Serum
## 21   45     R45Sd08        R45      R45S8        R45     sample   HF      Serum
## 22   46     R46Sd08        R46      R46S8        R46     sample   HF      Serum
## 23   47     R47Sd08        R47      R47S8        R47     sample   HF      Serum
## 24   48     R48Sd08        R48      R48S8        R48     sample   HF      Serum
## 25   56     R20Sd16        R20     R20S16        R20     sample   LF      Serum
## 26   57     R21Sd16        R21     R21S16        R21     sample   LF      Serum
## 27   58     R22Sd16        R22     R22S16        R22     sample   LF      Serum
## 28   59     R23Sd16        R23     R23S16        R23     sample   LF      Serum
## 29   60     R24Sd16        R24     R24S16        R24     sample   LF      Serum
## 30   67     R43Sd16        R43     R41S16        R41     sample   HF      Serum
## 31   68     R44Sd16        R44     R42S16        R42     sample   HF      Serum
## 32   69     R45Sd16        R45     R45S16        R45     sample   HF      Serum
## 33   70     R46Sd16        R46     R46S16        R46     sample   HF      Serum
## 34   71     R47Sd16        R47     R47S16        R47     sample   HF      Serum
## 35   72     R48Sd16        R48     R48S16        R48     sample   HF      Serum
## 36   79     R19Sd21        R19     R19S21        R19     sample   LF      Serum
## 37   80     R20Sd21        R20     R20S21        R20     sample   LF      Serum
## 38   81     R21Sd21        R21     R21S21        R21     sample   LF      Serum
## 39   82     R22Sd21        R22     R22S21        R22     sample   LF      Serum
## 40   83     R23Sd21        R23     R23S21        R23     sample   LF      Serum
## 41   84     R24Sd21        R24     R24S21        R24     sample   LF      Serum
## 42   91     R43Sd21        R43     R41S21        R41     sample   HF      Serum
## 43   92     R44Sd21        R44     R42S21        R42     sample   HF      Serum
## 44   93     R45Sd21        R45     R45S21        R45     sample   HF      Serum
## 45   94     R46Sd21        R46     R46S21        R46     sample   HF      Serum
## 46   95     R47Sd21        R47     R47S21        R47     sample   HF      Serum
## 47   96     R48Sd21        R48     R48S21        R48     sample   HF      Serum
## 48  111     R17Bd08        R17      R17B8        R17     sample   LF      Brain
## 49  112     R18Bd08        R18      R18B8        R18     sample   LF      Brain
## 50  113     R19Bd21        R19     R19B21        R19     sample   LF      Brain
## 51  114     R20Bd21        R20     R20B21        R20     sample   LF      Brain
## 52  115     R21Bd21        R21     R21B21        R21     sample   LF      Brain
## 53  116     R22Bd21        R22     R22B21        R22     sample   LF      Brain
## 54  117     R23Bd21        R23     R23B21        R23     sample   LF      Brain
## 55  118     R24Bd21        R24     R24B21        R24     sample   LF      Brain
## 56  132     R38Bd08        R38      R38B8        R38     sample   HF      Brain
## 57  133     R39Bd08        R39      R39B8        R39     sample   HF      Brain
## 58  136     R44Bd21        R44     R42B21        R42     sample   HF      Brain
## 59  137     R41Bd08        R41      R43B8        R43     sample   HF      Brain
## 60  138     R42Bd08        R42      R44B8        R44     sample   HF      Brain
## 61  139     R45Bd21        R45     R45B21        R45     sample   HF      Brain
## 62  140     R46Bd21        R46     R46B21        R46     sample   HF      Brain
## 63  141     R47Bd21        R47     R47B21        R47     sample   HF      Brain
## 64  142     R48Bd21        R48     R48B21        R48     sample   HF      Brain
## 65  156     R13Ld08        R13      R13L8        R13     sample   LF      Liver
## 66  157     R14Ld08        R14      R14L8        R14     sample   LF      Liver
## 67  158     R15Ld08        R15      R15L8        R15     sample   LF      Liver
## 68  159     R16Ld08        R16      R16L8        R16     sample   LF      Liver
## 69  160     R17Ld08        R17      R17L8        R17     sample   LF      Liver
## 70  161     R18Ld08        R18      R18L8        R18     sample   LF      Liver
## 71  162     R19Ld21        R19     R19L21        R19     sample   LF      Liver
## 72  163     R20Ld21        R20     R20L21        R20     sample   LF      Liver
## 73  164     R21Ld21        R21     R21L21        R21     sample   LF      Liver
## 74  165     R22Ld21        R22     R22L21        R22     sample   LF      Liver
## 75  166     R23Ld21        R23     R23L21        R23     sample   LF      Liver
## 76  167     R24Ld21        R24     R24L21        R24     sample   LF      Liver
## 77  180     R37Ld08        R37      R37L8        R37     sample   HF      Liver
## 78  181     R38Ld08        R38      R38L8        R38     sample   HF      Liver
## 79  182     R39Ld08        R39      R39L8        R39     sample   HF      Liver
## 80  183     R40Ld08        R40      R40L8        R40     sample   HF      Liver
## 81  184     R43Ld21        R43     R41L21        R41     sample   HF      Liver
## 82  185     R44Ld21        R44     R42L21        R42     sample   HF      Liver
## 83  186     R41Ld08        R41      R43L8        R43     sample   HF      Liver
## 84  187     R42Ld08        R42      R44L8        R44     sample   HF      Liver
## 85  188     R45Ld21        R45     R45L21        R45     sample   HF      Liver
## 86  189     R46Ld21        R46     R46L21        R46     sample   HF      Liver
## 87  190     R47Ld21        R47     R47L21        R47     sample   HF      Liver
## 88  191     R48Ld21        R48     R48L21        R48     sample   HF      Liver
## 89  193  PosCtrl101       ctrl PosCtrl101       ctrl    posctrl   HF    posctrl
## 90  194  PosCtrl102       ctrl PosCtrl102       ctrl    posctrl   HF    posctrl
## 91  195  PosCtrl103       ctrl PosCtrl103       ctrl    posctrl   HF    posctrl
## 92  208     R13Ud08        R13      R13U8        R13     sample   LF      Urine
## 93  209     R14Ud08        R14      R14U8        R14     sample   LF      Urine
## 94  210     R15Ud08        R15      R15U8        R15     sample   LF      Urine
## 95  211     R16Ud08        R16      R16U8        R16     sample   LF      Urine
## 96  212     R17Ud08        R17      R17U8        R17     sample   LF      Urine
## 97  213     R18Ud08        R18      R18U8        R18     sample   LF      Urine
## 98  214     R20Ud08        R20      R20U8        R20     sample   LF      Urine
## 99  215     R21Ud08        R21      R21U8        R21     sample   LF      Urine
## 100 216     R22Ud08        R22      R22U8        R22     sample   LF      Urine
## 101 217     R23Ud08        R23      R23U8        R23     sample   LF      Urine
## 102 218     R24Ud08        R24      R24U8        R24     sample   LF      Urine
## 103 231     R37Ud08        R37      R37U8        R37     sample   HF      Urine
## 104 232     R38Ud08        R38      R38U8        R38     sample   HF      Urine
## 105 233     R39Ud08        R39      R39U8        R39     sample   HF      Urine
## 106 234     R40Ud08        R40      R40U8        R40     sample   HF      Urine
## 107 235     R43Ud08        R43      R41U8        R41     sample   HF      Urine
## 108 236     R44Ud08        R44      R42U8        R42     sample   HF      Urine
## 109 237     R41Ud08        R41      R43U8        R43     sample   HF      Urine
## 110 238     R42Ud08        R42      R44U8        R44     sample   HF      Urine
## 111 239     R45Ud08        R45      R45U8        R45     sample   HF      Urine
## 112 240     R46Ud08        R46      R46U8        R46     sample   HF      Urine
## 113 241     R47Ud08        R47      R47U8        R47     sample   HF      Urine
## 114 242     R48Ud08        R48      R48U8        R48     sample   HF      Urine
## 115 249     R19Ud16        R19     R19U16        R19     sample   LF      Urine
## 116 250     R20Ud16        R20     R20U16        R20     sample   LF      Urine
## 117 251     R21Ud16        R21     R21U16        R21     sample   LF      Urine
## 118 252     R22Ud16        R22     R22U16        R22     sample   LF      Urine
## 119 253     R23Ud16        R23     R23U16        R23     sample   LF      Urine
## 120 254     R24Ud16        R24     R24U16        R24     sample   LF      Urine
## 121 261     R43Ud16        R43     R41U16        R41     sample   HF      Urine
## 122 262     R44Ud16        R44     R42U16        R42     sample   HF      Urine
## 123 263     R45Ud16        R45     R45U16        R45     sample   HF      Urine
## 124 264     R46Ud16        R46     R46U16        R46     sample   HF      Urine
## 125 265     R47Ud16        R47     R47U16        R47     sample   HF      Urine
## 126 266     R48Ud16        R48     R48U16        R48     sample   HF      Urine
## 127 273     R19Ud21        R19     R19U21        R19     sample   LF      Urine
## 128 274     R20Ud21        R20     R20U21        R20     sample   LF      Urine
## 129 275     R21Ud21        R21     R21U21        R21     sample   LF      Urine
## 130 276     R22Ud21        R22     R22U21        R22     sample   LF      Urine
## 131 277     R23Ud21        R23     R23U21        R23     sample   LF      Urine
## 132 278     R24Ud21        R24     R24U21        R24     sample   LF      Urine
## 133 285     R43Ud21        R43     R41U21        R41     sample   HF      Urine
## 134 286     R44Ud21        R44     R42U21        R42     sample   HF      Urine
## 135 287     R45Ud21        R45     R45U21        R45     sample   HF      Urine
## 136 288     R46Ud21        R46     R46U21        R46     sample   HF      Urine
## 137 289     R47Ud21        R47     R47U21        R47     sample   HF      Urine
## 138 290     R48Ud21        R48     R48U21        R48     sample   HF      Urine
## 139 303     R13Cd08        R13      R13C8        R13     sample   LF      Cecum
## 140 304     R14Cd08        R14      R14C8        R14     sample   LF      Cecum
## 141 305     R15Cd08        R15      R15C8        R15     sample   LF      Cecum
## 142 306     R16Cd08        R16      R16C8        R16     sample   LF      Cecum
## 143 307     R17Cd08        R17      R17C8        R17     sample   LF      Cecum
## 144 308     R18Cd08        R18      R18C8        R18     sample   LF      Cecum
## 145 309     R19Cd21        R19     R19C21        R19     sample   LF      Cecum
## 146 310     R20Cd21        R20     R20C21        R20     sample   LF      Cecum
## 147 311     R21Cd21        R21     R21C21        R21     sample   LF      Cecum
## 148 312     R22Cd21        R22     R22C21        R22     sample   LF      Cecum
## 149 313     R23Cd21        R23     R23C21        R23     sample   LF      Cecum
## 150 314     R24Cd21        R24     R24C21        R24     sample   LF      Cecum
## 151 327     R37Cd08        R37      R37C8        R37     sample   HF      Cecum
## 152 328     R38Cd08        R38      R38C8        R38     sample   HF      Cecum
## 153 329     R39Cd08        R39      R39C8        R39     sample   HF      Cecum
## 154 330     R40Cd08        R40      R40C8        R40     sample   HF      Cecum
## 155 331     R43Cd21        R43     R41C21        R41     sample   HF      Cecum
## 156 332     R44Cd21        R44     R42C21        R42     sample   HF      Cecum
## 157 333     R41Cd08        R41      R43C8        R43     sample   HF      Cecum
## 158 334     R42Cd08        R42      R44C8        R44     sample   HF      Cecum
## 159 335     R45Cd21        R45     R45C21        R45     sample   HF      Cecum
## 160 336     R46Cd21        R46     R46C21        R46     sample   HF      Cecum
## 161 337     R47Cd21        R47     R47C21        R47     sample   HF      Cecum
## 162 338     R48Cd21        R48     R48C21        R48     sample   HF      Cecum
## 163 339     R19Sd16        R19     R19S16        R19     sample   LF      Serum
## 164 343     Susp201 suspension    Susp201 suspension suspension      suspension
## 165 344     Susp202 suspension    Susp202 suspension suspension      suspension
## 166 345     Susp203 suspension    Susp203 suspension suspension      suspension
## 167 347  PosCtrl301       ctrl PosCtrl301       ctrl    posctrl   HF    posctrl
## 168 348  PosCtrl302       ctrl PosCtrl302       ctrl    posctrl   HF    posctrl
## 169 349  PosCtrl303       ctrl PosCtrl303       ctrl    posctrl   HF    posctrl
## 170 351     R37Bd08        R37      R37B8        R37     sample   HF      Brain
## 171 352     R14Bd08        R14      R14B8        R14     sample   LF      Brain
## 172 353     R15Bd08        R15      R15B8        R15     sample   LF      Brain
## 173 354     R16Bd08        R16      R16B8        R16     sample   LF      Brain
## 174 367     R13Fd08        R13      R13F8        R13     sample   LF      Feces
## 175 368     R14Fd08        R14      R14F8        R14     sample   LF      Feces
## 176 369     R15Fd08        R15      R15F8        R15     sample   LF      Feces
## 177 370     R16Fd08        R16      R16F8        R16     sample   LF      Feces
## 178 371     R17Fd08        R17      R17F8        R17     sample   LF      Feces
## 179 372     R18Fd08        R18      R18F8        R18     sample   LF      Feces
## 180 373     R19Fd08        R19      R19F8        R19     sample   LF      Feces
## 181 374     R20Fd08        R20      R20F8        R20     sample   LF      Feces
## 182 375     R21Fd08        R21      R21F8        R21     sample   LF      Feces
## 183 376     R22Fd08        R22      R22F8        R22     sample   LF      Feces
## 184 377     R23Fd08        R23      R23F8        R23     sample   LF      Feces
## 185 378     R24Fd08        R24      R24F8        R24     sample   LF      Feces
## 186 391     R37Fd08        R37      R37F8        R37     sample   HF      Feces
## 187 392     R38Fd08        R38      R38F8        R38     sample   HF      Feces
## 188 393     R39Fd08        R39      R39F8        R39     sample   HF      Feces
## 189 394     R40Fd08        R40      R40F8        R40     sample   HF      Feces
## 190 395     R43Fd08        R43      R41F8        R41     sample   HF      Feces
## 191 396     R44Fd08        R44      R42F8        R42     sample   HF      Feces
## 192 397     R41Fd08        R41      R43F8        R43     sample   HF      Feces
## 193 398     R42Fd08        R42      R44F8        R44     sample   HF      Feces
## 194 399     R45Fd08        R45      R45F8        R45     sample   HF      Feces
## 195 400     R46Fd08        R46      R46F8        R46     sample   HF      Feces
## 196 401     R47Fd08        R47      R47F8        R47     sample   HF      Feces
## 197 402     R48Fd08        R48      R48F8        R48     sample   HF      Feces
## 198 409     R19Fd12        R19     R19F12        R19     sample   LF      Feces
## 199 410     R20Fd12        R20     R20F12        R20     sample   LF      Feces
## 200 411     R21Fd12        R21     R21F12        R21     sample   LF      Feces
## 201 412     R22Fd12        R22     R22F12        R22     sample   LF      Feces
## 202 413     R23Fd12        R23     R23F12        R23     sample   LF      Feces
## 203 414     R24Fd12        R24     R24F12        R24     sample   LF      Feces
## 204 421     R43Fd12        R43     R41F12        R41     sample   HF      Feces
## 205 422     R44Fd12        R44     R42F12        R42     sample   HF      Feces
## 206 423     R45Fd12        R45     R45F12        R45     sample   HF      Feces
## 207 424     R46Fd12        R46     R46F12        R46     sample   HF      Feces
## 208 425     R47Fd12        R47     R47F12        R47     sample   HF      Feces
## 209 426     R48Fd12        R48     R48F12        R48     sample   HF      Feces
## 210 433     R19Fd16        R19     R19F16        R19     sample   LF      Feces
## 211 434     R20Fd16        R20     R20F16        R20     sample   LF      Feces
## 212 435     R21Fd16        R21     R21F16        R21     sample   LF      Feces
## 213 436     R22Fd16        R22     R22F16        R22     sample   LF      Feces
## 214 437     R23Fd16        R23     R23F16        R23     sample   LF      Feces
## 215 438     R24Fd16        R24     R24F16        R24     sample   LF      Feces
## 216 445     R43Fd16        R43     R41F16        R41     sample   HF      Feces
## 217 446     R44Fd16        R44     R42F16        R42     sample   HF      Feces
## 218 447     R45Fd16        R45     R45F16        R45     sample   HF      Feces
## 219 448     R46Fd16        R46     R46F16        R46     sample   HF      Feces
## 220 449     R47Fd16        R47     R47F16        R47     sample   HF      Feces
## 221 450     R48Fd16        R48     R48F16        R48     sample   HF      Feces
## 222 457     R19Fd21        R19     R19F21        R19     sample   LF      Feces
## 223 458     R20Fd21        R20     R20F21        R20     sample   LF      Feces
## 224 459     R21Fd21        R21     R21F21        R21     sample   LF      Feces
## 225 460     R22Fd21        R22     R22F21        R22     sample   LF      Feces
## 226 461     R23Fd21        R23     R23F21        R23     sample   LF      Feces
## 227 462     R24Fd21        R24     R24F21        R24     sample   LF      Feces
## 228 469     R43Fd21        R43     R41F21        R41     sample   HF      Feces
## 229 470     R44Fd21        R44     R42F21        R42     sample   HF      Feces
## 230 471     R45Fd21        R45     R45F21        R45     sample   HF      Feces
## 231 472     R46Fd21        R46     R46F21        R46     sample   HF      Feces
## 232 473     R47Fd21        R47     R47F21        R47     sample   HF      Feces
## 233 474     R48Fd21        R48     R48F21        R48     sample   HF      Feces
## 234 491  PosCtrl401       ctrl PosCtrl401       ctrl    posctrl   HF    posctrl
## 235 492  PosCtrl402       ctrl PosCtrl402       ctrl    posctrl   HF    posctrl
## 236 493  PosCtrl403       ctrl PosCtrl403       ctrl    posctrl   HF    posctrl
##     day     df batch_no                       notes org_conc sample_conc_ug
## 1   d08  16000        2                                4.171        66.7360
## 2   d08  16000        2                                3.367        53.8720
## 3   d08  16000        2                                3.515        56.2400
## 4   d08  16000        2                                4.042        64.6720
## 5   d08  16000        2                                3.580        57.2800
## 6   d08  16000        2                                3.839        61.4240
## 7   d08  16000        2                                3.557        56.9120
## 8   d08  16000        2                                3.469        55.5040
## 9   d08  16000        2                                3.218        51.4880
## 10  d08  16000        2                                3.630        58.0800
## 11  d08  16000        2                                3.810        60.9600
## 12  d08  16000        2                                3.600        57.6000
## 13  d08  16000        2                                4.003        64.0480
## 14  d08  16000        2                                3.735        59.7600
## 15  d08  16000        2                                3.256        52.0960
## 16  d08  16000        2                                3.117        49.8720
## 17  d08  16000        2                                3.363        53.8080
## 18  d08  16000        2                                3.712        59.3920
## 19  d08  16000        2                                3.893        62.2880
## 20  d08  16000        2                                3.656        58.4960
## 21  d08  16000        2                                3.521        56.3360
## 22  d08  16000        2                                3.248        51.9680
## 23  d08  16000        2                                2.926        46.8160
## 24  d08  16000        2                                3.494        55.9040
## 25  d16  16000        2                                3.053        48.8480
## 26  d16  16000        2                                3.040        48.6400
## 27  d16  16000        2                                3.085        49.3600
## 28  d16  16000        2                                3.352        53.6320
## 29  d16  16000        2                                2.771        44.3360
## 30  d16  16000        2                                2.985        47.7600
## 31  d16  16000        2                                3.150        50.4000
## 32  d16  16000        2                                2.945        47.1200
## 33  d16  16000        2                                2.873        45.9680
## 34  d16  16000        2                                2.477        39.6320
## 35  d16  16000        2                                2.700        43.2000
## 36  d21  16000        2                                2.640        42.2400
## 37  d21  16000        2                                2.430        38.8800
## 38  d21  16000        2                                2.271        36.3360
## 39  d21  16000        2                                2.767        44.2720
## 40  d21  16000        2                                3.194        51.1040
## 41  d21  16000        2                                2.552        40.8320
## 42  d21  16000        2                                2.714        43.4240
## 43  d21  16000        2                                2.651        42.4160
## 44  d21  16000        2                                2.668        42.6880
## 45  d21  16000        2                                2.304        36.8640
## 46  d21  16000        2                                2.140        34.2400
## 47  d21  16000        2                                2.533        40.5280
## 48  d08   8000        2                                0.464         3.7120
## 49  d08   8000        2                                0.382         3.0560
## 50  d21   8000        2                                0.318         2.5440
## 51  d21   8000        2                                0.183         1.4640
## 52  d21   8000        2                                0.320         2.5600
## 53  d21   8000        2                                0.305         2.4400
## 54  d21   8000        2                                0.364         2.9120
## 55  d21   8000        2                                0.364         2.9120
## 56  d08   8000        2                                0.547         4.3760
## 57  d08   8000        2                                0.537         4.2960
## 58  d21   8000        2                                0.227         1.8160
## 59  d08   8000        2                                0.635         5.0800
## 60  d08   8000        2                                0.538         4.3040
## 61  d21   8000        2                                0.326         2.6080
## 62  d21   8000        2                                0.203         1.6240
## 63  d21   8000        2                                0.268         2.1440
## 64  d21   8000        2                                0.301         2.4080
## 65  d08  80000        2                                1.949       155.9200
## 66  d08  80000        2                                2.332       186.5600
## 67  d08  80000        2                                2.189       175.1200
## 68  d08  80000        2                                2.134       170.7200
## 69  d08  80000        2                                2.195       175.6000
## 70  d08  80000        2                                1.954       156.3200
## 71  d21  80000        2                                1.772       141.7600
## 72  d21  80000        2                                1.906       152.4800
## 73  d21  80000        2                                1.702       136.1600
## 74  d21  80000        2                                1.962       156.9600
## 75  d21  80000        2                                1.813       145.0400
## 76  d21  80000        2                                1.728       138.2400
## 77  d08  80000        2                                2.393       191.4400
## 78  d08  80000        2                                2.554       204.3200
## 79  d08  80000        2                                2.357       188.5600
## 80  d08  80000        2                                2.315       185.2000
## 81  d21  80000        2                                1.832       146.5600
## 82  d21  80000        2                                1.787       142.9600
## 83  d08  80000        2                                2.527       202.1600
## 84  d08  80000        2                                2.429       194.3200
## 85  d21  80000        2                                1.898       151.8400
## 86  d21  80000        2                                1.721       137.6800
## 87  d21  80000        2                                1.801       144.0800
## 88  d21  80000        2                                1.951       156.0800
## 89  d21     NA        2    5 ng/mL positive control    5.865             NA
## 90  d21     NA        2  2.5 ng/mL positive control    2.915             NA
## 91  d21     NA        2 1.25 ng/mL positive control    1.475             NA
## 92  d08    400        3                                2.004         0.8016
## 93  d08    400        3                                0.622         0.2488
## 94  d08    400        3                                1.418         0.5672
## 95  d08    400        3                                1.864         0.7456
## 96  d08    400        3                                1.885         0.7540
## 97  d08    400        3                                2.327         0.9308
## 98  d08    400        3                                1.635         0.6540
## 99  d08    400        3                                1.849         0.7396
## 100 d08    400        3                                2.059         0.8236
## 101 d08    400        3                                1.024         0.4096
## 102 d08    400        3                                5.863         2.3452
## 103 d08    400        3                                1.786         0.7144
## 104 d08    400        3                                1.123         0.4492
## 105 d08    400        3                                1.518         0.6072
## 106 d08    400        3                                1.519         0.6076
## 107 d08    400        3                                1.128         0.4512
## 108 d08    400        3                                0.856         0.3424
## 109 d08    400        3                                1.279         0.5116
## 110 d08    400        3                                1.413         0.5652
## 111 d08    400        3                                1.524         0.6096
## 112 d08    400        3                                1.566         0.6264
## 113 d08    400        3                                0.826         0.3304
## 114 d08    400        3                                2.966         1.1864
## 115 d16    400        3                                5.603         2.2412
## 116 d16    400        3                                2.268         0.9072
## 117 d16    400        3                                4.073         1.6292
## 118 d16    400        3                                2.651         1.0604
## 119 d16    400        3                                0.808         0.3232
## 120 d16    400        3                                2.438         0.9752
## 121 d16    400        3                                3.121         1.2484
## 122 d16    400        3                                1.588         0.6352
## 123 d16    400        3                                2.935         1.1740
## 124 d16    400        3                                7.665         3.0660
## 125 d16    400        3                                1.781         0.7124
## 126 d16    400        3                                2.645         1.0580
## 127 d21    400        3                                0.882         0.3528
## 128 d21    400        3                                0.381         0.1524
## 129 d21    400        3                                0.297         0.1188
## 130 d21    400        3                                1.015         0.4060
## 131 d21    400        3                                0.620         0.2480
## 132 d21    400        3                                0.584         0.2336
## 133 d21    400        3                                0.465         0.1860
## 134 d21    400        3                                1.461         0.5844
## 135 d21    400        3                                0.601         0.2404
## 136 d21    400        3                                0.445         0.1780
## 137 d21    400        3                                0.293         0.1172
## 138 d21    400        3                                0.267         0.1068
## 139 d08   1000        3                                0.844         0.8440
## 140 d08   1000        3                                0.765         0.7650
## 141 d08   1000        3                                0.903         0.9030
## 142 d08   1000        3                                0.975         0.9750
## 143 d08   1000        3                                1.025         1.0250
## 144 d08   1000        3                                0.930         0.9300
## 145 d21   1000        3                                0.740         0.7400
## 146 d21   1000        3                                0.881         0.8810
## 147 d21   1000        3                                0.526         0.5260
## 148 d21   1000        3                                0.852         0.8520
## 149 d21   1000        3                                0.866         0.8660
## 150 d21   1000        3                                0.590         0.5900
## 151 d08   1000        3                                1.881         1.8810
## 152 d08   1000        3                                1.592         1.5920
## 153 d08   1000        3                                1.447         1.4470
## 154 d08   1000        3                                0.919         0.9190
## 155 d21   1000        3                                1.139         1.1390
## 156 d21   1000        3                                1.224         1.2240
## 157 d08   1000        3                                1.812         1.8120
## 158 d08   1000        3                                1.406         1.4060
## 159 d21   1000        3                                1.127         1.1270
## 160 d21   1000        3                                1.057         1.0570
## 161 d21   1000        3                                1.187         1.1870
## 162 d21   1000        3                                1.100         1.1000
## 163 d16  16000        3                                3.148        50.3680
## 164     400000        3                  triplicate    3.394      1357.6000
## 165     400000        3                  triplicate    3.364      1345.6000
## 166     400000        3                  triplicate    3.396      1358.4000
## 167 d08     NA        3    5 ng/mL positive control    5.973             NA
## 168 d08     NA        3  2.5 ng/mL positive control    2.977             NA
## 169 d08     NA        3 1.25 ng/mL positive control    1.562             NA
## 170 d08   8000        4                                0.487         3.8960
## 171 d08   8000        4                                0.524         4.1920
## 172 d08   8000        4                                0.583         4.6640
## 173 d08   8000        4                                0.499         3.9920
## 174 d08   1000        4                                1.089         1.0890
## 175 d08   1000        4                                1.888         1.8880
## 176 d08   1000        4                                2.179         2.1790
## 177 d08   1000        4                                1.601         1.6010
## 178 d08   1000        4                                1.688         1.6880
## 179 d08   1000        4                                1.571         1.5710
## 180 d08   1000        4                                1.857         1.8570
## 181 d08   1000        4                                1.369         1.3690
## 182 d08   1000        4                                1.359         1.3590
## 183 d08   1000        4                                1.407         1.4070
## 184 d08   1000        4                                1.552         1.5520
## 185 d08   1000        4                                1.779         1.7790
## 186 d08   1000        4                                4.078         4.0780
## 187 d08   1000        4                                3.737         3.7370
## 188 d08   1000        4                                3.522         3.5220
## 189 d08   1000        4                                2.262         2.2620
## 190 d08   1000        4                                4.449         4.4490
## 191 d08   1000        4                                3.248         3.2480
## 192 d08   1000        4                                4.056         4.0560
## 193 d08   1000        4                                4.650         4.6500
## 194 d08   1000        4                                3.577         3.5770
## 195 d08   1000        4                                3.170         3.1700
## 196 d08   1000        4                                2.915         2.9150
## 197 d08   1000        4                                2.208         2.2080
## 198 d12   1000        4                                0.864         0.8640
## 199 d12   1000        4                                1.527         1.5270
## 200 d12   1000        4                                1.658         1.6580
## 201 d12   1000        4                                1.084         1.0840
## 202 d12   1000        4                                1.739         1.7390
## 203 d12   1000        4                                1.198         1.1980
## 204 d12   1000        4                                2.455         2.4550
## 205 d12   1000        4                                2.300         2.3000
## 206 d12   1000        4                                3.063         3.0630
## 207 d12   1000        4                                2.709         2.7090
## 208 d12   1000        4                                2.529         2.5290
## 209 d12   1000        4                                1.892         1.8920
## 210 d16   1000        4                                1.200         1.2000
## 211 d16   1000        4                                1.198         1.1980
## 212 d16   1000        4                                1.101         1.1010
## 213 d16   1000        4                                1.383         1.3830
## 214 d16   1000        4                                1.780         1.7800
## 215 d16   1000        4                                1.855         1.8550
## 216 d16   1000        4                                2.628         2.6280
## 217 d16   1000        4                                2.406         2.4060
## 218 d16   1000        4                                2.675         2.6750
## 219 d16   1000        4                                1.835         1.8350
## 220 d16   1000        4                                2.181         2.1810
## 221 d16   1000        4                                1.628         1.6280
## 222 d21   1000        4                                1.056         1.0560
## 223 d21   1000        4                                1.179         1.1790
## 224 d21   1000        4                                1.025         1.0250
## 225 d21   1000        4                                1.234         1.2340
## 226 d21   1000        4                                0.997         0.9970
## 227 d21   1000        4                                0.877         0.8770
## 228 d21   1000        4                                2.103         2.1030
## 229 d21   1000        4                                2.337         2.3370
## 230 d21   1000        4                                2.746         2.7460
## 231 d21   1000        4                                1.316         1.3160
## 232 d21   1000        4                                2.554         2.5540
## 233 d21   1000        4                                1.335         1.3350
## 234 d08     NA        4    5 ng/mL positive control    5.693             NA
## 235 d08     NA        4  2.5 ng/mL positive control    2.889             NA
## 236 d08     NA        4 1.25 ng/mL positive control    1.521             NA
##     pfos_t_amt pfos_b_amt pfos_l_amt bl_ratio dayfeed     matfeed     br_pct
## 1        4.175      1.331      2.802   0.4750  d08_LF    Serum_LF 0.31880240
## 2        3.342      1.194      2.223   0.5371  d08_LF    Serum_LF 0.35727110
## 3        3.482      1.342      2.268   0.5917  d08_LF    Serum_LF 0.38541068
## 4        4.022      1.253      2.550   0.4914  d08_LF    Serum_LF 0.31153655
## 5        3.563      1.151      2.387   0.4822  d08_LF    Serum_LF 0.32304238
## 6        3.816      1.340      2.386   0.5616  d08_LF    Serum_LF 0.35115304
## 7        3.535      1.138      2.422   0.4699  d08_LF    Serum_LF 0.32192362
## 8        3.443      1.261      2.061   0.6118  d08_LF    Serum_LF 0.36625036
## 9        3.222      1.061      2.189   0.4847  d08_LF    Serum_LF 0.32929857
## 10       3.609      1.347      2.358   0.5712  d08_LF    Serum_LF 0.37323358
## 11       3.796      1.183      2.543   0.4652  d08_LF    Serum_LF 0.31164384
## 12       3.567      1.299      2.315   0.5611  d08_LF    Serum_LF 0.36417157
## 13       3.975      1.505      2.393   0.6289  d08_HF    Serum_HF 0.37861635
## 14       3.705      1.433      2.391   0.5993  d08_HF    Serum_HF 0.38677463
## 15       3.229      1.163      2.014   0.5775  d08_HF    Serum_HF 0.36017343
## 16       3.100      0.935      2.033   0.4599  d08_HF    Serum_HF 0.30161290
## 17       3.338      1.321      2.199   0.6007  d08_HF    Serum_HF 0.39574596
## 18       3.669      1.253      2.434   0.5148  d08_HF    Serum_HF 0.34150995
## 19       3.870      1.326      2.513   0.5277  d08_HF    Serum_HF 0.34263566
## 20       3.626      1.280      2.358   0.5428  d08_HF    Serum_HF 0.35300607
## 21       3.478      1.335      2.321   0.5752  d08_HF    Serum_HF 0.38384129
## 22       3.229      0.942      2.113   0.4458  d08_HF    Serum_HF 0.29173119
## 23       2.913      1.062      1.880   0.5649  d08_HF    Serum_HF 0.36457261
## 24       3.475      1.329      2.207   0.6022  d08_HF    Serum_HF 0.38244604
## 25       3.039      1.082      1.911   0.5662  d16_LF    Serum_LF 0.35603817
## 26       3.019      1.199      1.952   0.6142  d16_LF    Serum_LF 0.39715137
## 27       3.069      1.154      1.801   0.6408  d16_LF    Serum_LF 0.37601825
## 28       3.323      1.139      2.234   0.5098  d16_LF    Serum_LF 0.34276256
## 29       2.767      0.917      1.869   0.4906  d16_LF    Serum_LF 0.33140585
## 30       2.957      0.964      1.979   0.4871  d16_HF    Serum_HF 0.32600609
## 31       3.116      1.143      1.913   0.5975  d16_HF    Serum_HF 0.36681643
## 32       2.924      1.035      1.697   0.6099  d16_HF    Serum_HF 0.35396717
## 33       2.859      0.876      1.779   0.4924  d16_HF    Serum_HF 0.30640084
## 34       2.463      0.978      1.541   0.6347  d16_HF    Serum_HF 0.39707674
## 35       2.685      0.948      1.668   0.5683  d16_HF    Serum_HF 0.35307263
## 36       2.617      1.094      1.574   0.6950  d21_LF    Serum_LF 0.41803592
## 37       2.421      0.929      1.447   0.6420  d21_LF    Serum_LF 0.38372573
## 38       2.256      0.803      1.447   0.5549  d21_LF    Serum_LF 0.35593972
## 39       2.754      1.003      1.511   0.6638  d21_LF    Serum_LF 0.36419753
## 40       3.181      1.280      1.921   0.6663  d21_LF    Serum_LF 0.40238919
## 41       2.540      1.073      1.404   0.7642  d21_LF    Serum_LF 0.42244094
## 42       2.701      1.115      1.660   0.6717  d21_HF    Serum_HF 0.41281007
## 43       2.631      1.157      1.612   0.7177  d21_HF    Serum_HF 0.43975675
## 44       2.656      0.907      1.697   0.5345  d21_HF    Serum_HF 0.34149096
## 45       2.294      0.788      1.443   0.5461  d21_HF    Serum_HF 0.34350480
## 46       2.129      0.777      1.171   0.6635  d21_HF    Serum_HF 0.36496008
## 47       2.518      0.999      1.501   0.6656  d21_HF    Serum_HF 0.39674345
## 48       0.467      0.079      0.454   0.1740  d08_LF    Brain_LF 0.16916488
## 49       0.385      0.057      0.395   0.1443  d08_LF    Brain_LF 0.14805195
## 50       0.321      0.059      0.324   0.1821  d21_LF    Brain_LF 0.18380062
## 51       0.190      0.053      0.213   0.2488  d21_LF    Brain_LF 0.27894737
## 52       0.324      0.061      0.334   0.1826  d21_LF    Brain_LF 0.18827160
## 53       0.308      0.049      0.327   0.1498  d21_LF    Brain_LF 0.15909091
## 54       0.370      0.055      0.376   0.1463  d21_LF    Brain_LF 0.14864865
## 55       0.367      0.060      0.367   0.1635  d21_LF    Brain_LF 0.16348774
## 56       0.550      0.082      0.527   0.1556  d08_HF    Brain_HF 0.14909091
## 57       0.541      0.124      0.500   0.2480  d08_HF    Brain_HF 0.22920518
## 58       0.231      0.063      0.238   0.2647  d21_HF    Brain_HF 0.27272727
## 59       0.639      0.103      0.625   0.1648  d08_HF    Brain_HF 0.16118936
## 60       0.539      0.073      0.515   0.1417  d08_HF    Brain_HF 0.13543599
## 61       0.328      0.046      0.352   0.1307  d21_HF    Brain_HF 0.14024390
## 62       0.207      0.045      0.231   0.1948  d21_HF    Brain_HF 0.21739130
## 63       0.274      0.046      0.185   0.2486  d21_HF    Brain_HF 0.16788321
## 64       0.305      0.057      0.312   0.1827  d21_HF    Brain_HF 0.18688525
## 65       1.937      0.543      1.429   0.3800  d08_LF    Liver_LF 0.28033041
## 66       2.310      0.380      1.861   0.2042  d08_LF    Liver_LF 0.16450216
## 67       2.176      0.477      1.623   0.2939  d08_LF    Liver_LF 0.21920956
## 68       2.117      0.697      1.494   0.4665  d08_LF    Liver_LF 0.32923949
## 69       2.184      0.378      1.855   0.2038  d08_LF    Liver_LF 0.17307692
## 70       1.944      0.323      1.460   0.2212  d08_LF    Liver_LF 0.16615226
## 71       1.766      0.458      1.436   0.3189  d21_LF    Liver_LF 0.25934315
## 72       1.895      0.782      1.237   0.6322  d21_LF    Liver_LF 0.41266491
## 73       1.699      0.561      1.198   0.4683  d21_LF    Liver_LF 0.33019423
## 74       1.948      0.353      1.405   0.2512  d21_LF    Liver_LF 0.18121150
## 75       1.806      0.351      1.340   0.2619  d21_LF    Liver_LF 0.19435216
## 76       1.723      0.425      1.254   0.3389  d21_LF    Liver_LF 0.24666280
## 77       2.376      0.765      1.628   0.4699  d08_HF    Liver_HF 0.32196970
## 78       2.533      0.799      1.815   0.4402  d08_HF    Liver_HF 0.31543624
## 79       2.346      0.783      1.655   0.4731  d08_HF    Liver_HF 0.33375959
## 80       2.307      0.588      1.676   0.3508  d08_HF    Liver_HF 0.25487646
## 81       1.821      0.646      1.306   0.4946  d21_HF    Liver_HF 0.35475014
## 82       1.779      0.549      1.213   0.4526  d21_HF    Liver_HF 0.30860034
## 83       2.516      0.803      1.789   0.4489  d08_HF    Liver_HF 0.31915739
## 84       2.418      0.615      1.649   0.3730  d08_HF    Liver_HF 0.25434243
## 85       1.889      0.468      1.290   0.3628  d21_HF    Liver_HF 0.24775013
## 86       1.720      0.523      1.157   0.4520  d21_HF    Liver_HF 0.30406977
## 87       1.793      0.548      1.392   0.3937  d21_HF    Liver_HF 0.30563302
## 88       1.940      0.493      1.374   0.3588  d21_HF    Liver_HF 0.25412371
## 89       5.843      0.503      5.748   0.0875  d21_HF  posctrl_HF 0.08608591
## 90       2.904      0.259      2.897   0.0894  d21_HF  posctrl_HF 0.08918733
## 91       1.474      0.130      1.417   0.0917  d21_HF  posctrl_HF 0.08819539
## 92       1.994      0.913      1.071   0.8525  d08_LF    Urine_LF 0.45787362
## 93       0.621      0.284      0.337   0.8427  d08_LF    Urine_LF 0.45732689
## 94       1.411      0.745      0.710   1.0493  d08_LF    Urine_LF 0.52799433
## 95       1.854      0.946      0.919   1.0294  d08_LF    Urine_LF 0.51024811
## 96       1.872      0.982      0.876   1.1210  d08_LF    Urine_LF 0.52457265
## 97       2.309      1.238      1.067   1.1603  d08_LF    Urine_LF 0.53616284
## 98       1.635      0.914      0.756   1.2090  d08_LF    Urine_LF 0.55902141
## 99       1.836      1.095      0.781   1.4020  d08_LF    Urine_LF 0.59640523
## 100      2.046      1.153      0.920   1.2533  d08_LF    Urine_LF 0.56353861
## 101      1.020      0.585      0.451   1.2971  d08_LF    Urine_LF 0.57352941
## 102      5.905      3.178      2.756   1.1531  d08_LF    Urine_LF 0.53818798
## 103      1.788      1.075      0.761   1.4126  d08_HF    Urine_HF 0.60123043
## 104      1.120      0.605      0.545   1.1101  d08_HF    Urine_HF 0.54017857
## 105      1.514      0.947      0.580   1.6328  d08_HF    Urine_HF 0.62549538
## 106      1.516      0.857      0.691   1.2402  d08_HF    Urine_HF 0.56530343
## 107      1.135      0.657      0.487   1.3491  d08_HF    Urine_HF 0.57885463
## 108      0.854      0.521      0.335   1.5552  d08_HF    Urine_HF 0.61007026
## 109      1.274      0.700      0.573   1.2216  d08_HF    Urine_HF 0.54945055
## 110      1.407      0.774      0.643   1.2037  d08_HF    Urine_HF 0.55010661
## 111      1.518      0.798      0.603   1.3234  d08_HF    Urine_HF 0.52569170
## 112      1.582      0.849      0.758   1.1201  d08_HF    Urine_HF 0.53666245
## 113      0.824      0.522      0.321   1.6262  d08_HF    Urine_HF 0.63349515
## 114      2.962      1.682      1.360   1.2368  d08_HF    Urine_HF 0.56785955
## 115      5.580      2.348      3.339   0.7032  d16_LF    Urine_LF 0.42078853
## 116      2.272      1.056      1.224   0.8627  d16_LF    Urine_LF 0.46478873
## 117      4.064      1.462      2.677   0.5461  d16_LF    Urine_LF 0.35974409
## 118      2.638      1.101      1.589   0.6929  d16_LF    Urine_LF 0.41736164
## 119      0.807      0.370      0.433   0.8545  d16_LF    Urine_LF 0.45848823
## 120      2.426      1.161      1.315   0.8829  d16_LF    Urine_LF 0.47856554
## 121      3.106      1.435      1.698   0.8451  d16_HF    Urine_HF 0.46200901
## 122      1.582      0.776      0.827   0.9383  d16_HF    Urine_HF 0.49051833
## 123      2.927      1.372      1.654   0.8295  d16_HF    Urine_HF 0.46873932
## 124      7.709      3.503      4.355   0.8044  d16_HF    Urine_HF 0.45440394
## 125      1.773      0.872      0.910   0.9582  d16_HF    Urine_HF 0.49182177
## 126      2.647      1.307      1.372   0.9526  d16_HF    Urine_HF 0.49376653
## 127      0.878      0.474      0.414   1.1449  d21_LF    Urine_LF 0.53986333
## 128      0.381      0.176      0.212   0.8302  d21_LF    Urine_LF 0.46194226
## 129      0.296      0.157      0.139   1.1295  d21_LF    Urine_LF 0.53040541
## 130      1.011      0.520      0.487   1.0678  d21_LF    Urine_LF 0.51434224
## 131      0.619      0.267      0.356   0.7500  d21_LF    Urine_LF 0.43134087
## 132      0.582      0.285      0.302   0.9437  d21_LF    Urine_LF 0.48969072
## 133      0.464      0.248      0.221   1.1222  d21_HF    Urine_HF 0.53448276
## 134      1.458      0.737      0.740   0.9959  d21_HF    Urine_HF 0.50548697
## 135      0.600      0.319      0.295   1.0814  d21_HF    Urine_HF 0.53166667
## 136      0.445      0.212      0.233   0.9099  d21_HF    Urine_HF 0.47640449
## 137      0.293      0.170      0.127   1.3386  d21_HF    Urine_HF 0.58020478
## 138      0.267      0.141      0.127   1.1102  d21_HF    Urine_HF 0.52808989
## 139      0.842      0.168      0.676   0.2485  d08_LF    Cecum_LF 0.19952494
## 140      0.763      0.135      0.624   0.2163  d08_LF    Cecum_LF 0.17693316
## 141      0.901      0.176      0.727   0.2421  d08_LF    Cecum_LF 0.19533851
## 142      0.972      0.196      0.800   0.2450  d08_LF    Cecum_LF 0.20164609
## 143      1.023      0.175      0.857   0.2042  d08_LF    Cecum_LF 0.17106549
## 144      0.929      0.161      0.769   0.2094  d08_LF    Cecum_LF 0.17330463
## 145      0.739      0.153      0.594   0.2576  d21_LF    Cecum_LF 0.20703654
## 146      0.881      0.109      0.706   0.1544  d21_LF    Cecum_LF 0.12372304
## 147      0.526      0.088      0.439   0.2005  d21_LF    Cecum_LF 0.16730038
## 148      0.850      0.186      0.679   0.2739  d21_LF    Cecum_LF 0.21882353
## 149      0.864      0.136      0.707   0.1924  d21_LF    Cecum_LF 0.15740741
## 150      0.591      0.109      0.474   0.2300  d21_LF    Cecum_LF 0.18443316
## 151      1.879      0.383      1.494   0.2564  d08_HF    Cecum_HF 0.20383183
## 152      1.590      0.298      1.285   0.2319  d08_HF    Cecum_HF 0.18742138
## 153      1.444      0.293      1.156   0.2535  d08_HF    Cecum_HF 0.20290859
## 154      0.917      0.176      0.748   0.2353  d08_HF    Cecum_HF 0.19193021
## 155      1.135      0.225      0.909   0.2475  d21_HF    Cecum_HF 0.19823789
## 156      1.221      0.271      0.970   0.2794  d21_HF    Cecum_HF 0.22194922
## 157      1.809      0.400      1.426   0.2805  d08_HF    Cecum_HF 0.22111664
## 158      1.404      0.276      1.128   0.2447  d08_HF    Cecum_HF 0.19658120
## 159      1.126      0.207      0.935   0.2214  d21_HF    Cecum_HF 0.18383659
## 160      1.057      0.198      0.874   0.2265  d21_HF    Cecum_HF 0.18732261
## 161      1.184      0.235      0.958   0.2453  d21_HF    Cecum_HF 0.19847973
## 162      1.097      0.196      0.914   0.2144  d21_HF    Cecum_HF 0.17866910
## 163      3.146      0.982      2.241   0.4382  d16_LF    Serum_LF 0.31214240
## 164      3.397      0.816      2.647   0.3083       _ suspension_ 0.24021195
## 165      3.360      0.786      2.601   0.3022       _ suspension_ 0.23392857
## 166      3.399      0.796      2.630   0.3027       _ suspension_ 0.23418653
## 167      5.971      0.659      5.384   0.1224  d08_HF  posctrl_HF 0.11036677
## 168      2.979      0.315      2.663   0.1183  d08_HF  posctrl_HF 0.10574018
## 169      1.561      0.174      1.412   0.1232  d08_HF  posctrl_HF 0.11146701
## 170      0.489      0.064      0.433   0.1478  d08_HF    Brain_HF 0.13087935
## 171      0.526      0.066      0.476   0.1387  d08_LF    Brain_LF 0.12547529
## 172      0.585      0.066      0.530   0.1245  d08_LF    Brain_LF 0.11282051
## 173      0.502      0.068      0.441   0.1542  d08_LF    Brain_LF 0.13545817
## 174      1.088      0.220      0.882   0.2494  d08_LF    Feces_LF 0.20220588
## 175      1.883      0.398      1.507   0.2641  d08_LF    Feces_LF 0.21136484
## 176      2.169      0.433      1.758   0.2463  d08_LF    Feces_LF 0.19963117
## 177      1.597      0.307      1.320   0.2326  d08_LF    Feces_LF 0.19223544
## 178      1.682      0.313      1.388   0.2255  d08_LF    Feces_LF 0.18608799
## 179      1.564      0.285      1.298   0.2196  d08_LF    Feces_LF 0.18222506
## 180      1.848      0.358      1.526   0.2346  d08_LF    Feces_LF 0.19372294
## 181      1.365      0.258      1.123   0.2297  d08_LF    Feces_LF 0.18901099
## 182      1.352      0.271      1.098   0.2468  d08_LF    Feces_LF 0.20044379
## 183      1.405      0.244      1.178   0.2071  d08_LF    Feces_LF 0.17366548
## 184      1.542      0.297      1.264   0.2350  d08_LF    Feces_LF 0.19260700
## 185      1.774      0.360      1.433   0.2512  d08_LF    Feces_LF 0.20293123
## 186      4.075      0.865      3.276   0.2640  d08_HF    Feces_HF 0.21226994
## 187      3.732      0.741      3.048   0.2431  d08_HF    Feces_HF 0.19855305
## 188      3.516      0.708      2.837   0.2496  d08_HF    Feces_HF 0.20136519
## 189      2.256      0.470      1.804   0.2605  d08_HF    Feces_HF 0.20833333
## 190      4.427      0.873      3.625   0.2408  d08_HF    Feces_HF 0.19719901
## 191      3.250      0.624      2.651   0.2354  d08_HF    Feces_HF 0.19200000
## 192      4.057      0.750      3.315   0.2262  d08_HF    Feces_HF 0.18486566
## 193      4.650      0.905      3.775   0.2397  d08_HF    Feces_HF 0.19462366
## 194      3.570      0.534      2.964   0.1802  d08_HF    Feces_HF 0.14957983
## 195      3.168      0.630      2.565   0.2456  d08_HF    Feces_HF 0.19886364
## 196      2.906      0.564      2.370   0.2380  d08_HF    Feces_HF 0.19408121
## 197      2.208      0.410      1.798   0.2280  d08_HF    Feces_HF 0.18568841
## 198      0.864      0.148      0.735   0.2014  d12_LF    Feces_LF 0.17129630
## 199      1.527      0.335      1.209   0.2771  d12_LF    Feces_LF 0.21938441
## 200      1.660      0.390      1.286   0.3033  d12_LF    Feces_LF 0.23493976
## 201      1.083      0.247      0.839   0.2944  d12_LF    Feces_LF 0.22807018
## 202      1.735      0.333      1.402   0.2375  d12_LF    Feces_LF 0.19193084
## 203      1.195      0.240      0.959   0.2503  d12_LF    Feces_LF 0.20083682
## 204      2.449      0.526      1.984   0.2651  d12_HF    Feces_HF 0.21478154
## 205      2.341      0.551      1.872   0.2943  d12_HF    Feces_HF 0.23536950
## 206      3.051      0.662      2.463   0.2688  d12_HF    Feces_HF 0.21697804
## 207      2.712      0.579      2.172   0.2666  d12_HF    Feces_HF 0.21349558
## 208      2.518      0.556      1.992   0.2791  d12_HF    Feces_HF 0.22081017
## 209      1.895      0.381      1.528   0.2493  d12_HF    Feces_HF 0.20105541
## 210      1.292      0.210      1.076   0.1952  d16_LF    Feces_LF 0.16253870
## 211      1.225      0.239      0.996   0.2400  d16_LF    Feces_LF 0.19510204
## 212      1.104      0.216      0.902   0.2395  d16_LF    Feces_LF 0.19565217
## 213      1.384      0.261      1.124   0.2322  d16_LF    Feces_LF 0.18858382
## 214      1.777      0.357      1.467   0.2434  d16_LF    Feces_LF 0.20090039
## 215      1.852      0.379      1.484   0.2554  d16_LF    Feces_LF 0.20464363
## 216      2.626      0.637      2.034   0.3132  d16_HF    Feces_HF 0.24257426
## 217      2.401      0.469      1.894   0.2476  d16_HF    Feces_HF 0.19533528
## 218      2.674      0.554      2.109   0.2627  d16_HF    Feces_HF 0.20718025
## 219      1.828      0.385      1.458   0.2641  d16_HF    Feces_HF 0.21061269
## 220      2.182      0.472      1.726   0.2735  d16_HF    Feces_HF 0.21631531
## 221      1.626      0.343      1.298   0.2643  d16_HF    Feces_HF 0.21094711
## 222      1.061      0.234      0.851   0.2750  d21_LF    Feces_LF 0.22054665
## 223      1.186      0.288      0.944   0.3051  d21_LF    Feces_LF 0.24283305
## 224      1.030      0.239      0.842   0.2838  d21_LF    Feces_LF 0.23203883
## 225      1.236      0.309      0.956   0.3232  d21_LF    Feces_LF 0.25000000
## 226      0.999      0.203      0.839   0.2420  d21_LF    Feces_LF 0.20320320
## 227      0.880      0.181      0.713   0.2539  d21_LF    Feces_LF 0.20568182
## 228      2.099      0.452      1.621   0.2788  d21_HF    Feces_HF 0.21534064
## 229      2.345      0.578      1.803   0.3206  d21_HF    Feces_HF 0.24648188
## 230      2.748      0.610      2.106   0.2896  d21_HF    Feces_HF 0.22197962
## 231      4.932      0.987      3.931   0.2511  d21_HF    Feces_HF 0.20012165
## 232     12.084      2.551      8.211   0.3107  d21_HF    Feces_HF 0.21110559
## 233      1.504      0.448      0.851   0.5264  d21_HF    Feces_HF 0.29787234
## 234      6.571      0.666      5.744   0.1159  d08_HF  posctrl_HF 0.10135444
## 235      2.973      0.336      2.626   0.1280  d08_HF  posctrl_HF 0.11301715
## 236     60.549      6.677     53.119   0.1257  d08_HF  posctrl_HF 0.11027432
##         l_pct
## 1   0.6811976
## 2   0.6427289
## 3   0.6145893
## 4   0.6884635
## 5   0.6769576
## 6   0.6488470
## 7   0.6780764
## 8   0.6337496
## 9   0.6707014
## 10  0.6267664
## 11  0.6883562
## 12  0.6358284
## 13  0.6213836
## 14  0.6132254
## 15  0.6398266
## 16  0.6983871
## 17  0.6042540
## 18  0.6584901
## 19  0.6573643
## 20  0.6469939
## 21  0.6161587
## 22  0.7082688
## 23  0.6354274
## 24  0.6175540
## 25  0.6439618
## 26  0.6028486
## 27  0.6239818
## 28  0.6572374
## 29  0.6685941
## 30  0.6739939
## 31  0.6331836
## 32  0.6460328
## 33  0.6935992
## 34  0.6029233
## 35  0.6469274
## 36  0.5819641
## 37  0.6162743
## 38  0.6440603
## 39  0.6358025
## 40  0.5976108
## 41  0.5775591
## 42  0.5871899
## 43  0.5602433
## 44  0.6585090
## 45  0.6564952
## 46  0.6350399
## 47  0.6032566
## 48  0.8308351
## 49  0.8519481
## 50  0.8161994
## 51  0.7210526
## 52  0.8117284
## 53  0.8409091
## 54  0.8513514
## 55  0.8365123
## 56  0.8509091
## 57  0.7707948
## 58  0.7272727
## 59  0.8388106
## 60  0.8645640
## 61  0.8597561
## 62  0.7826087
## 63  0.8321168
## 64  0.8131148
## 65  0.7196696
## 66  0.8354978
## 67  0.7807904
## 68  0.6707605
## 69  0.8269231
## 70  0.8338477
## 71  0.7406569
## 72  0.5873351
## 73  0.6698058
## 74  0.8187885
## 75  0.8056478
## 76  0.7533372
## 77  0.6780303
## 78  0.6845638
## 79  0.6662404
## 80  0.7451235
## 81  0.6452499
## 82  0.6913997
## 83  0.6808426
## 84  0.7456576
## 85  0.7522499
## 86  0.6959302
## 87  0.6943670
## 88  0.7458763
## 89  0.9139141
## 90  0.9108127
## 91  0.9118046
## 92  0.5421264
## 93  0.5426731
## 94  0.4720057
## 95  0.4897519
## 96  0.4754274
## 97  0.4638372
## 98  0.4409786
## 99  0.4035948
## 100 0.4364614
## 101 0.4264706
## 102 0.4618120
## 103 0.3987696
## 104 0.4598214
## 105 0.3745046
## 106 0.4346966
## 107 0.4211454
## 108 0.3899297
## 109 0.4505495
## 110 0.4498934
## 111 0.4743083
## 112 0.4633375
## 113 0.3665049
## 114 0.4321404
## 115 0.5792115
## 116 0.5352113
## 117 0.6402559
## 118 0.5826384
## 119 0.5415118
## 120 0.5214345
## 121 0.5379910
## 122 0.5094817
## 123 0.5312607
## 124 0.5455961
## 125 0.5081782
## 126 0.5062335
## 127 0.4601367
## 128 0.5380577
## 129 0.4695946
## 130 0.4856578
## 131 0.5686591
## 132 0.5103093
## 133 0.4655172
## 134 0.4945130
## 135 0.4683333
## 136 0.5235955
## 137 0.4197952
## 138 0.4719101
## 139 0.8004751
## 140 0.8230668
## 141 0.8046615
## 142 0.7983539
## 143 0.8289345
## 144 0.8266954
## 145 0.7929635
## 146 0.8762770
## 147 0.8326996
## 148 0.7811765
## 149 0.8425926
## 150 0.8155668
## 151 0.7961682
## 152 0.8125786
## 153 0.7970914
## 154 0.8080698
## 155 0.8017621
## 156 0.7780508
## 157 0.7788834
## 158 0.8034188
## 159 0.8161634
## 160 0.8126774
## 161 0.8015203
## 162 0.8213309
## 163 0.6878576
## 164 0.7597880
## 165 0.7660714
## 166 0.7658135
## 167 0.8896332
## 168 0.8942598
## 169 0.8885330
## 170 0.8691207
## 171 0.8745247
## 172 0.8871795
## 173 0.8645418
## 174 0.7977941
## 175 0.7886352
## 176 0.8003688
## 177 0.8077646
## 178 0.8139120
## 179 0.8177749
## 180 0.8062771
## 181 0.8109890
## 182 0.7995562
## 183 0.8263345
## 184 0.8073930
## 185 0.7970688
## 186 0.7877301
## 187 0.8014469
## 188 0.7986348
## 189 0.7916667
## 190 0.8028010
## 191 0.8080000
## 192 0.8151343
## 193 0.8053763
## 194 0.8504202
## 195 0.8011364
## 196 0.8059188
## 197 0.8143116
## 198 0.8287037
## 199 0.7806156
## 200 0.7650602
## 201 0.7719298
## 202 0.8080692
## 203 0.7991632
## 204 0.7852185
## 205 0.7646305
## 206 0.7830220
## 207 0.7865044
## 208 0.7791898
## 209 0.7989446
## 210 0.8374613
## 211 0.8048980
## 212 0.8043478
## 213 0.8114162
## 214 0.7990996
## 215 0.7953564
## 216 0.7574257
## 217 0.8046647
## 218 0.7928197
## 219 0.7893873
## 220 0.7836847
## 221 0.7890529
## 222 0.7794533
## 223 0.7571669
## 224 0.7679612
## 225 0.7500000
## 226 0.7967968
## 227 0.7943182
## 228 0.7846594
## 229 0.7535181
## 230 0.7780204
## 231 0.7998783
## 232 0.7888944
## 233 0.7021277
## 234 0.8986456
## 235 0.8869828
## 236 0.8897257
# Save object
save(dat, file = "R_objects/pfos_isomer_data.Rdata")

7.2 SYSTEMIC MEASUREMENTS

7.2.1 Blood serum data

Investigation of br-PFOS % in treatment groups in serum samples on Day 8, Day 16 and Day 21.

7.2.1.1 Prepare data

# Load data
load("R_objects/pfos_isomer_data.Rdata")

# Subset
dat.clean <- subset(dat, material == "Serum")
# Sort dat.clean
dat.clean <- dat.clean[order(dat.clean$feed),]

# Set names of variables
PREDICTOR <- c("day","feed")
OUTCOME <- "br_pct" #"bl_ratio"
SUBJECT <- "rat_name"

# Create formula
PREDICTOR.F <- ifelse(length(PREDICTOR) > 1, paste(PREDICTOR, collapse = "*"), PREDICTOR)
FORMULA <- as.formula(paste(OUTCOME,PREDICTOR.F, sep = " ~ "))

# Summary samples in groups
dat.clean %>% group_by(across(all_of(PREDICTOR))) %>% get_summary_stats(!!sym(OUTCOME), type = "full") %>% select("day","feed","n","min","max","mean","sd","se")
## # A tibble: 6 × 8
##   day   feed      n   min   max  mean    sd    se
##   <chr> <chr> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
## 1 d08   HF       12 0.292 0.396 0.357 0.033 0.01 
## 2 d08   LF       12 0.312 0.385 0.343 0.026 0.008
## 3 d16   HF        6 0.306 0.397 0.351 0.032 0.013
## 4 d16   LF        6 0.312 0.397 0.353 0.031 0.013
## 5 d21   HF        6 0.341 0.44  0.383 0.04  0.016
## 6 d21   LF        6 0.356 0.422 0.391 0.028 0.011
dat.clean %>% group_by(across(all_of(PREDICTOR))) %>% get_summary_stats(!!sym("l_pct"), type = "full") %>% select("day","feed","n","min","max","mean","sd","se")
## # A tibble: 6 × 8
##   day   feed      n   min   max  mean    sd    se
##   <chr> <chr> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
## 1 d08   HF       12 0.604 0.708 0.643 0.033 0.01 
## 2 d08   LF       12 0.615 0.688 0.657 0.026 0.008
## 3 d16   HF        6 0.603 0.694 0.649 0.032 0.013
## 4 d16   LF        6 0.603 0.688 0.647 0.031 0.013
## 5 d21   HF        6 0.56  0.659 0.617 0.04  0.016
## 6 d21   LF        6 0.578 0.644 0.609 0.028 0.011
# Test for outliers
dat.clean %>% 
  group_by(across(all_of(PREDICTOR))) %>% 
  identify_outliers(!!sym(OUTCOME))
##  [1] feed           day            id             sample_name    rat_name      
##  [6] sample_org     rat_org        type           material       df            
## [11] batch_no       notes          org_conc       sample_conc_ug pfos_t_amt    
## [16] pfos_b_amt     pfos_l_amt     bl_ratio       dayfeed        matfeed       
## [21] br_pct         l_pct          is.outlier     is.extreme    
## <0 rækker> (eller 0-længde row.names)
# Check normality
# Build the linear model
model  <- lm(FORMULA, data = dat.clean)
# Create a QQ plot of residuals
ggqqplot(residuals(model))

# Compute Shapiro-Wilk test of normality
shapiro_test(residuals(model))
## # A tibble: 1 × 3
##   variable         statistic p.value
##   <chr>                <dbl>   <dbl>
## 1 residuals(model)     0.976   0.442
# Check the homogeneity of variances with Levene's test
dat.clean %>% levene_test(FORMULA)
## # A tibble: 1 × 4
##     df1   df2 statistic     p
##   <int> <int>     <dbl> <dbl>
## 1     5    42     0.380 0.860
# Save result
EQUAL.VAR <- dat.clean %>% levene_test(FORMULA) %>% pull(p) > 0.05

This shows that data has two outlier, is normally distribution and has equal variance. Therefore we can test the data with a one-way ANOVA test with Tukey’s honest significance test.

7.2.1.2 ANOVA One-Way test

Perform test

If we had equality of variance we can now run a one-way ANOVA tests anova_test() (if we have equal variance) or a welch_anova_test() (if variance vary).

if(EQUAL.VAR) {
  res.aov <- dat.clean %>% anova_test(FORMULA)
  res.aov
} else {
  res.aov <- dat.clean %>% welch_anova_test(FORMULA)
  res.aov
}
## ANOVA Table (type II tests)
## 
##     Effect DFn DFd     F     p p<.05   ges
## 1      day   2  42 6.217 0.004     * 0.228
## 2     feed   1  42 0.254 0.617       0.006
## 3 day:feed   2  42 0.582 0.563       0.027

Perform posthoc test

if(EQUAL.VAR) {
  pwc <- dat.clean %>% tukey_hsd(FORMULA)
  pwc
} else {
  pwc <- dat.clean %>% games_howell_test(FORMULA)
  pwc
}
## # A tibble: 19 × 9
##    term     group1 group2 null.value estimate conf.low conf.high   p.adj
##  * <chr>    <chr>  <chr>       <dbl>    <dbl>    <dbl>     <dbl>   <dbl>
##  1 day      d08    d16             0  0.00172 -0.0252    0.0286  0.987  
##  2 day      d08    d21             0  0.0373   0.0104    0.0642  0.00449
##  3 day      d16    d21             0  0.0356   0.00456   0.0666  0.0213 
##  4 feed     HF     LF              0 -0.00455 -0.0228    0.0137  0.617  
##  5 day:feed d08:HF d16:HF          0 -0.00633 -0.0530    0.0404  0.999  
##  6 day:feed d08:HF d21:HF          0  0.0263  -0.0204    0.0730  0.551  
##  7 day:feed d08:HF d08:LF          0 -0.0141  -0.0522    0.0241  0.878  
##  8 day:feed d08:HF d16:LF          0 -0.00430 -0.0510    0.0424  1      
##  9 day:feed d08:HF d21:LF          0  0.0342  -0.0125    0.0809  0.265  
## 10 day:feed d16:HF d21:HF          0  0.0327  -0.0213    0.0866  0.472  
## 11 day:feed d16:HF d08:LF          0 -0.00775 -0.0545    0.0390  0.996  
## 12 day:feed d16:HF d16:LF          0  0.00203 -0.0519    0.0560  1      
## 13 day:feed d16:HF d21:LF          0  0.0406  -0.0134    0.0945  0.239  
## 14 day:feed d21:HF d08:LF          0 -0.0404  -0.0871    0.00631 0.124  
## 15 day:feed d21:HF d16:LF          0 -0.0306  -0.0846    0.0233  0.543  
## 16 day:feed d21:HF d21:LF          0  0.00791 -0.0460    0.0618  0.998  
## 17 day:feed d08:LF d16:LF          0  0.00977 -0.0369    0.0565  0.989  
## 18 day:feed d08:LF d21:LF          0  0.0483   0.00160   0.0950  0.0389 
## 19 day:feed d16:LF d21:LF          0  0.0385  -0.0154    0.0925  0.291  
## # ℹ 1 more variable: p.adj.signif <chr>

Significant impact is observed between Day 21 and the two other days. We will plot this as a nested analysis with t-test and ANOVA with Tukey’s HSD on the inner variable and the outer variable.

7.2.1.3 Create figure

## Pairwise comparison for inner variable: day
stat.in <- dat.clean %>%
  group_by(day) %>%
  t_test(as.formula(paste0(OUTCOME," ~ feed")), 
         paired = FALSE, var.equal = EQUAL.VAR, 
         detailed = TRUE, alternative = "two.sided") %>%
  add_significance() %>%
  p_format("p", accuracy = 0.001, trailing.zero = TRUE, new.col = TRUE)
stat.in
## # A tibble: 3 × 18
##   day   estimate estimate1 estimate2 .y.    group1 group2    n1    n2 statistic
## * <chr>    <dbl>     <dbl>     <dbl> <chr>  <chr>  <chr>  <int> <int>     <dbl>
## 1 d08    0.0141      0.357     0.343 br_pct HF     LF        12    12     1.15 
## 2 d16   -0.00203     0.351     0.353 br_pct HF     LF         6     6    -0.113
## 3 d21   -0.00791     0.383     0.391 br_pct HF     LF         6     6    -0.400
## # ℹ 8 more variables: p <dbl>, df <dbl>, conf.low <dbl>, conf.high <dbl>,
## #   method <chr>, alternative <chr>, p.signif <chr>, p.format <chr>
## Pairwise comparison for outer variable
stat.out <- dat.clean %>%
  anova_test(as.formula(paste0(OUTCOME," ~ day"))) %>%
  add_significance() %>%
  p_format("p", accuracy = 0.001, trailing.zero = TRUE, new.col = TRUE)
stat.out
## ANOVA Table (type II tests)
## 
##   Effect DFn DFd     F     p p<.05   ges p.signif p.format
## 1    day   2  45 6.443 0.003     * 0.223       **    0.003
pwc2 <- dat.clean %>%
  tukey_hsd(as.formula(paste0(OUTCOME," ~ day"))) %>%
  add_significance() %>%
  p_format("p.adj", accuracy = 0.001, trailing.zero = TRUE, new.col = TRUE)
pwc2
## # A tibble: 3 × 10
##   term  group1 group2 null.value estimate conf.low conf.high   p.adj
## * <chr> <chr>  <chr>       <dbl>    <dbl>    <dbl>     <dbl>   <dbl>
## 1 day   d08    d16             0  0.00172 -0.0246     0.0281 0.986  
## 2 day   d08    d21             0  0.0373   0.0110     0.0637 0.00362
## 3 day   d16    d21             0  0.0356   0.00518    0.0660 0.0183 
## # ℹ 2 more variables: p.adj.signif <chr>, p.adj.format <chr>
## Calculate positions statistics on plot
stat.in <- stat.in %>% add_xy_position(x = "day", dodge = 0.8)
pwc2 <- pwc2 %>% add_xy_position(x = "day")
pwc2$y.position <- max(stat.in$y.position)*1.1

# Create plot
p <- ggboxplot(dat.clean, x = "day", y = OUTCOME,
               fill = "feed",
               color = "feed",
               add = "jitter",
               add.params = list(size = 1)) +
  theme_pubr(legend = "top") +
  scale_color_manual(values = c("LF" = "black","HF" = "black")) +
  scale_fill_manual(values = params$COLFEED, name = "Feed") +
  scale_x_discrete(name = "Day", labels = c("d08" = "Day 8","d16" = "Day 16","d21" = "Day 21")) +
  theme(axis.title.x = element_blank()) +
  guides(color = "none")

if (OUTCOME == "bl_ratio") {
p.stat <- p + stat_pvalue_manual(stat.in, tip.length = 0, hide.ns = TRUE, color = "red") +
  stat_pvalue_manual(pwc2, tip.length = 0, hide.ns = TRUE, y.position = c(0.82, 0.78)) +
  scale_y_continuous(name = "Serum B/L ratio")
} else if (OUTCOME == "br_pct") {
p.stat <- p + stat_pvalue_manual(stat.in, tip.length = 0, hide.ns = TRUE, color = "red") +
  stat_pvalue_manual(pwc2, tip.length = 0, hide.ns = TRUE, y.position = c(0.46, 0.44)) +
  scale_y_continuous(name = "Serum br-PFOS %", labels = function(x) paste0(x*100, "%"))
}
p.stat

suppressMessages(ggsave(filename = paste0("plots/animal_data/pfos/isomer_",OUTCOME,"_serum.pdf"), plot = p.stat, device = "pdf", dpi = 300, units = "mm", height = 100, width = 100))
suppressMessages(ggsave(filename = paste0("plots/animal_data/pfos/isomer_",OUTCOME,"_serum.png"), plot = p.stat, device = "png", dpi = 300, units = "mm", height = 100, width = 100))

# BETWEEN DIETS
## Pairwise comparison for inner variable: day
stat.in2 <- dat.clean %>%
  group_by(feed) %>%
  anova_test(as.formula(paste0(OUTCOME," ~ day"))) %>%
  add_significance() %>%
  p_format("p", accuracy = 0.001, trailing.zero = TRUE, new.col = TRUE)
stat.in2
## # A tibble: 2 × 10
##   feed  Effect   DFn   DFd     F     p `p<.05`   ges p.signif p.format
## * <chr> <chr>  <dbl> <dbl> <dbl> <dbl> <chr>   <dbl> <chr>    <chr>   
## 1 HF    day        2    21  1.60 0.226 ""      0.132 ns       0.226   
## 2 LF    day        2    21  6.19 0.008 "*"     0.371 **       0.008
pwc.in2 <- dat.clean %>%
  group_by(feed) %>%
  tukey_hsd(as.formula(paste0(OUTCOME," ~ day"))) %>%
  add_significance() %>%
  p_format("p.adj", accuracy = 0.001, trailing.zero = TRUE, new.col = TRUE)
pwc.in2
## # A tibble: 6 × 11
##   feed  term  group1 group2 null.value estimate conf.low conf.high   p.adj
## * <chr> <chr> <chr>  <chr>       <dbl>    <dbl>    <dbl>     <dbl>   <dbl>
## 1 HF    day   d08    d16             0 -0.00633 -0.0498     0.0371 0.929  
## 2 HF    day   d08    d21             0  0.0263  -0.0171     0.0698 0.299  
## 3 HF    day   d16    d21             0  0.0327  -0.0175     0.0829 0.252  
## 4 LF    day   d08    d16             0  0.00977 -0.0252     0.0447 0.763  
## 5 LF    day   d08    d21             0  0.0483   0.0134     0.0832 0.00599
## 6 LF    day   d16    d21             0  0.0385  -0.00181    0.0789 0.0629 
## # ℹ 2 more variables: p.adj.signif <chr>, p.adj.format <chr>
## Pairwise comparison for outer variable
stat.out2 <- dat.clean %>%
  t_test(as.formula(paste0(OUTCOME," ~ feed")),
         paired = FALSE, var.equal = EQUAL.VAR,
         detailed = TRUE, alternative = "two.sided") %>%
  add_significance() %>%
  p_format("p", accuracy = 0.001, trailing.zero = TRUE, new.col = TRUE)
stat.out2
## # A tibble: 1 × 17
##   estimate estimate1 estimate2 .y.    group1 group2    n1    n2 statistic     p
## *    <dbl>     <dbl>     <dbl> <chr>  <chr>  <chr>  <int> <int>     <dbl> <dbl>
## 1  0.00455     0.362     0.357 br_pct HF     LF        24    24     0.459 0.649
## # ℹ 7 more variables: df <dbl>, conf.low <dbl>, conf.high <dbl>, method <chr>,
## #   alternative <chr>, p.signif <chr>, p.format <chr>
## Calculate positions statistics on plot
pwc.in2 <- pwc.in2 %>% add_xy_position(x = "feed", dodge = 0.8)
stat.out2 <- stat.out2 %>% add_xy_position(x = "feed")
stat.out2$y.position <- max(pwc.in2$y.position)*1.1

# Create plot
p <- ggboxplot(dat.clean, x = "feed", y = OUTCOME,
               fill = "feed",
               color = "day",
               shape = "day",
               add = "jitter",
               add.params = list(size = 1, position_jitterdodge(dodge.width = 0.8))) +
  theme_pubr() + 
  scale_color_manual(values = params$COL_4BLACK) +#, name = "Day", labels = c("d08" = "8", "d12" = "12", "d16" = "16","d21" = "21")) +
  scale_fill_manual(values = params$COLFEED, name = "Feed") +
  scale_shape_manual(values = c("d08" = 19, "d12" = 1, "d16" = 17, "d21" = 2), name = "Day", labels = c("d08" = "8", "d12" = "12", "d16" = "16","d21" = "21")) +
  scale_x_discrete(name = "Feed") +
  theme(axis.title.x = element_blank()) +
  guides(color = "none", fill = "none", shape = "none")

p

if (OUTCOME == "bl_ratio") {
p.stat2 <- p + stat_pvalue_manual(pwc.in2, tip.length = 0, hide.ns = TRUE, color = "red") +
  stat_pvalue_manual(stat.out2, tip.length = 0, hide.ns = TRUE) +
  scale_y_continuous(name = "Serum B/L ratio")
} else if (OUTCOME == "br_pct") {
  p.stat2 <- p + stat_pvalue_manual(pwc.in2, tip.length = 0, hide.ns = TRUE, color = "red") +
  stat_pvalue_manual(stat.out2, tip.length = 0, hide.ns = TRUE) +
  scale_y_continuous(name = "Serum br-PFOS %", labels = function(x) paste0(x*100, "%"))
}
p.stat2

suppressMessages(ggsave(filename = paste0("plots/animal_data/pfos/isomer_",OUTCOME,"_serum_feed.pdf"), plot = p.stat2, device = "pdf", dpi = 300, units = "mm", height = 80, width = 100))
suppressMessages(ggsave(filename = paste0("plots/animal_data/pfos/isomer_",OUTCOME,"_serum_feed.png"), plot = p.stat2, device = "png", dpi = 300, units = "mm", height = 80, width = 100))

p.serum1 <- p.stat
p.serum2 <- p.stat2
save(p.serum1, p.serum2, file = "plots/animal_data/pfos/serum_isomer.Rdata")

# clear the environment and release memory
rm(list = ls(all.names = TRUE)[ls(all.names = TRUE) != "params"])
invisible(gc())

7.2.1.4 Conclusion

From BL-ratio of serum, we can see that no significant difference was observed between feed groups on sampling days. However, BL-ratio is significantly higher on Day 21 compared to Day 8 and Day 16.

7.2.2 Liver data

Investigation of bl-ratio in treatment groups in Liver tissue samples on Day 8 and Day 21.

7.2.2.1 Prepare data

# Load data
load("R_objects/pfos_isomer_data.Rdata")

# Subset
dat.clean <- subset(dat, material == "Liver")
# Sort dat.clean
dat.clean <- dat.clean[order(dat.clean$feed),]

# Set names of variables
PREDICTOR <- c("day","feed")
OUTCOME <- "br_pct" #"bl_ratio"
SUBJECT <- "rat_name"

# Create formula
PREDICTOR.F <- ifelse(length(PREDICTOR) > 1, paste(PREDICTOR, collapse = "*"), PREDICTOR)
FORMULA <- as.formula(paste(OUTCOME,PREDICTOR.F, sep = " ~ "))

# Summary samples in groups
dat.clean %>% group_by(across(all_of(PREDICTOR))) %>% get_summary_stats(!!sym(OUTCOME), type = "full") %>% select("day","feed","n","min","max","mean","sd","se")
## # A tibble: 4 × 8
##   day   feed      n   min   max  mean    sd    se
##   <chr> <chr> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
## 1 d08   HF        6 0.254 0.334 0.3   0.036 0.015
## 2 d08   LF        6 0.165 0.329 0.222 0.069 0.028
## 3 d21   HF        6 0.248 0.355 0.296 0.04  0.016
## 4 d21   LF        6 0.181 0.413 0.271 0.087 0.036
dat.clean %>% group_by(across(all_of(PREDICTOR))) %>% get_summary_stats(!!sym("l_pct"), type = "full") %>% select("day","feed","n","min","max","mean","sd","se")
## # A tibble: 4 × 8
##   day   feed      n   min   max  mean    sd    se
##   <chr> <chr> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
## 1 d08   HF        6 0.666 0.746 0.7   0.036 0.015
## 2 d08   LF        6 0.671 0.835 0.778 0.069 0.028
## 3 d21   HF        6 0.645 0.752 0.704 0.04  0.016
## 4 d21   LF        6 0.587 0.819 0.729 0.087 0.036
# Test for outliers
dat.clean %>% 
  group_by(across(all_of(PREDICTOR))) %>% 
  identify_outliers(!!sym(OUTCOME))
##  [1] feed           day            id             sample_name    rat_name      
##  [6] sample_org     rat_org        type           material       df            
## [11] batch_no       notes          org_conc       sample_conc_ug pfos_t_amt    
## [16] pfos_b_amt     pfos_l_amt     bl_ratio       dayfeed        matfeed       
## [21] br_pct         l_pct          is.outlier     is.extreme    
## <0 rækker> (eller 0-længde row.names)
# Check normality
# Build the linear model
model  <- lm(FORMULA, data = dat.clean)
# Create a QQ plot of residuals
ggqqplot(residuals(model))

# Compute Shapiro-Wilk test of normality
shapiro_test(residuals(model))
## # A tibble: 1 × 3
##   variable         statistic p.value
##   <chr>                <dbl>   <dbl>
## 1 residuals(model)     0.952   0.294
# Check the homogeneity of variances with Levene's test
dat.clean %>% levene_test(FORMULA)
## # A tibble: 1 × 4
##     df1   df2 statistic     p
##   <int> <int>     <dbl> <dbl>
## 1     3    20      1.29 0.306
# Save result
EQUAL.VAR <- dat.clean %>% levene_test(FORMULA) %>% pull(p) > 0.05

This shows that data has two outlier, is normally distribution and has equal variance. Therefore we can test the data with a one-way ANOVA test with Tukey’s honest significance test.

7.2.2.2 ANOVA One-Way test

Perform test

If we had equality of variance we can now run a one-way ANOVA tests anova_test() (if we have equal variance) or a welch_anova_test() (if variance vary).

if(EQUAL.VAR) {
  res.aov <- dat.clean %>% anova_test(FORMULA)
  res.aov
} else {
  res.aov <- dat.clean %>% welch_anova_test(FORMULA)
  res.aov
}
## ANOVA Table (type II tests)
## 
##     Effect DFn DFd     F     p p<.05   ges
## 1      day   1  20 0.782 0.387       0.038
## 2     feed   1  20 4.172 0.054       0.173
## 3 day:feed   1  20 1.096 0.308       0.052

Perform posthoc test

if(EQUAL.VAR) {
  pwc <- dat.clean %>% tukey_hsd(FORMULA)
  pwc
} else {
  pwc <- dat.clean %>% games_howell_test(FORMULA)
  pwc
}
## # A tibble: 8 × 9
##   term  group1 group2 null.value estimate conf.low conf.high  p.adj p.adj.signif
## * <chr> <chr>  <chr>       <dbl>    <dbl>    <dbl>     <dbl>  <dbl> <chr>       
## 1 day   d08    d21             0  0.0223   -0.0303   0.0748  0.387  ns          
## 2 feed  HF     LF              0 -0.0515   -0.104    0.00109 0.0545 ns          
## 3 day:… d08:HF d21:HF          0 -0.00410  -0.104    0.0956  0.999  ns          
## 4 day:… d08:HF d08:LF          0 -0.0778   -0.178    0.0219  0.162  ns          
## 5 day:… d08:HF d21:LF          0 -0.0292   -0.129    0.0705  0.845  ns          
## 6 day:… d21:HF d08:LF          0 -0.0737   -0.173    0.0260  0.197  ns          
## 7 day:… d21:HF d21:LF          0 -0.0251   -0.125    0.0746  0.894  ns          
## 8 day:… d08:LF d21:LF          0  0.0487   -0.0511   0.148   0.534  ns

Significant impact is observed for feed but no other factors. We will plot this as a nested analysis with pairwise t-tests on the inner variable and the outer variable.

7.2.2.3 Create figure

# BETWEEN DAYS
# Pairwise comparison for inner variable: day
stat.in <- dat.clean %>%
  group_by(day) %>%
  t_test(as.formula(paste0(OUTCOME," ~ feed")),
         paired = FALSE, var.equal = EQUAL.VAR,
         detailed = TRUE, alternative = "two.sided") %>%
  add_significance() %>%
  p_format("p", accuracy = 0.001, trailing.zero = TRUE, new.col = TRUE)
stat.in
## # A tibble: 2 × 18
##   day   estimate estimate1 estimate2 .y.    group1 group2    n1    n2 statistic
## * <chr>    <dbl>     <dbl>     <dbl> <chr>  <chr>  <chr>  <int> <int>     <dbl>
## 1 d08     0.0778     0.300     0.222 br_pct HF     LF         6     6     2.46 
## 2 d21     0.0251     0.296     0.271 br_pct HF     LF         6     6     0.640
## # ℹ 8 more variables: p <dbl>, df <dbl>, conf.low <dbl>, conf.high <dbl>,
## #   method <chr>, alternative <chr>, p.signif <chr>, p.format <chr>
## Pairwise comparison for outer variable
stat.out <- dat.clean %>%
  t_test(as.formula(paste0(OUTCOME," ~ day")),
         paired = FALSE, var.equal = EQUAL.VAR,
         detailed = TRUE, alternative = "two.sided") %>%
  add_significance() %>%
  p_format("p", accuracy = 0.001, trailing.zero = TRUE, new.col = TRUE)
stat.out
## # A tibble: 1 × 17
##   estimate estimate1 estimate2 .y.    group1 group2    n1    n2 statistic     p
## *    <dbl>     <dbl>     <dbl> <chr>  <chr>  <chr>  <int> <int>     <dbl> <dbl>
## 1  -0.0223     0.261     0.283 br_pct d08    d21       12    12    -0.825 0.418
## # ℹ 7 more variables: df <dbl>, conf.low <dbl>, conf.high <dbl>, method <chr>,
## #   alternative <chr>, p.signif <chr>, p.format <chr>
## Calculate positions statistics on plot
stat.in <- stat.in %>% add_xy_position(x = "day", dodge = 0.8)
stat.out <- stat.out %>% add_xy_position(x = "day")
stat.out$y.position <- max(stat.in$y.position)*1.1

# Create plot
p <- ggboxplot(dat.clean, x = "day", y = OUTCOME,
               fill = "feed",
               color = "feed",
               add = "jitter",
               add.params = list(size = 1)) +
  theme_pubr(legend = "top") +
  scale_color_manual(values = c("LF" = "black","HF" = "black")) +
  scale_fill_manual(values = params$COLFEED, name = "Feed") +
  scale_x_discrete(name = "Day", labels = c("d08" = "Day 8","d16" = "Day 16","d21" = "Day 21")) +
  theme(axis.title.x = element_blank()) +
  guides(color = "none")
if (OUTCOME == "bl_ratio") {
p.stat <- p + stat_pvalue_manual(stat.in, tip.length = 0, hide.ns = TRUE, color = "red") +
  stat_pvalue_manual(stat.out, tip.length = 0, hide.ns = TRUE) +
  scale_y_continuous(name = "Liver B/L ratio")
} else if (OUTCOME == "br_pct") {
p.stat <- p + stat_pvalue_manual(stat.in, tip.length = 0, hide.ns = TRUE, color = "red", y.position = 0.35) +
  stat_pvalue_manual(stat.out, tip.length = 0, hide.ns = TRUE) +
  scale_y_continuous(name = "Liver br-PFOS %", labels = function(x) paste0(x*100, "%"))
}
p.stat

suppressMessages(ggsave(filename = paste0("plots/animal_data/pfos/isomer_",OUTCOME,"_liver.pdf"), plot = p.stat, device = "pdf", dpi = 300, units = "mm", height = 100, width = 75))
suppressMessages(ggsave(filename = paste0("plots/animal_data/pfos/isomer_",OUTCOME,"_liver.png"), plot = p.stat, device = "png", dpi = 300, units = "mm", height = 100, width = 75))

# BETWEEN DIETS
## Pairwise comparison for inner variable: day
stat.in2 <- dat.clean %>%
  group_by(feed) %>%
  t_test(as.formula(paste0(OUTCOME," ~ day")), 
         paired = FALSE, var.equal = EQUAL.VAR, 
         detailed = TRUE, alternative = "two.sided") %>%
  add_significance() %>%
  p_format("p", accuracy = 0.001, trailing.zero = TRUE, new.col = TRUE)
stat.in2
## # A tibble: 2 × 18
##   feed  estimate estimate1 estimate2 .y.    group1 group2    n1    n2 statistic
## * <chr>    <dbl>     <dbl>     <dbl> <chr>  <chr>  <chr>  <int> <int>     <dbl>
## 1 HF     0.00410     0.300     0.296 br_pct d08    d21        6     6     0.189
## 2 LF    -0.0487      0.222     0.271 br_pct d08    d21        6     6    -1.07 
## # ℹ 8 more variables: p <dbl>, df <dbl>, conf.low <dbl>, conf.high <dbl>,
## #   method <chr>, alternative <chr>, p.signif <chr>, p.format <chr>
## Pairwise comparison for outer variable
stat.out2 <- dat.clean %>%
  t_test(as.formula(paste0(OUTCOME," ~ feed")),
         paired = FALSE, var.equal = EQUAL.VAR,
         detailed = TRUE, alternative = "two.sided") %>%
  add_significance() %>%
  p_format("p", accuracy = 0.001, trailing.zero = TRUE, new.col = TRUE)
stat.out2
## # A tibble: 1 × 17
##   estimate estimate1 estimate2 .y.    group1 group2    n1    n2 statistic      p
## *    <dbl>     <dbl>     <dbl> <chr>  <chr>  <chr>  <int> <int>     <dbl>  <dbl>
## 1   0.0515     0.298     0.246 br_pct HF     LF        12    12      2.05 0.0526
## # ℹ 7 more variables: df <dbl>, conf.low <dbl>, conf.high <dbl>, method <chr>,
## #   alternative <chr>, p.signif <chr>, p.format <chr>
## Calculate positions statistics on plot
stat.in2 <- stat.in2 %>% add_xy_position(x = "feed", dodge = 0.8)
stat.out2 <- stat.out2 %>% add_xy_position(x = "feed")
stat.out2$y.position <- max(stat.in2$y.position)*1.1

# Create plot
p <- ggboxplot(dat.clean, x = "feed", y = OUTCOME,
               fill = "feed",
               color = "day",
               shape = "day",
               add = "jitter",
               add.params = list(size = 1, position_jitterdodge(dodge.width = 0.8))) +
  theme_pubr() + 
  scale_color_manual(values = params$COL_4BLACK) +#, name = "Day", labels = c("d08" = "8", "d12" = "12", "d16" = "16","d21" = "21")) +
  scale_fill_manual(values = params$COLFEED, name = "Feed") +
  scale_shape_manual(values = c("d08" = 19, "d12" = 1, "d16" = 17, "d21" = 2), name = "Day", labels = c("d08" = "8", "d12" = "12", "d16" = "16","d21" = "21")) +
  scale_x_discrete(name = "Feed") +
  theme(axis.title.x = element_blank()) +
  guides(color = "none", fill = "none", shape = "none")

if (OUTCOME == "bl_ratio") {
p.stat2 <- p + stat_pvalue_manual(stat.in2, tip.length = 0, hide.ns = TRUE, color = "red") +
  stat_pvalue_manual(stat.out2, tip.length = 0, hide.ns = TRUE) +
  scale_y_continuous(name = "Liver B/L ratio")
} else if (OUTCOME == "br_pct") {
p.stat2 <- p + stat_pvalue_manual(stat.in2, tip.length = 0, hide.ns = TRUE, color = "red") +
  stat_pvalue_manual(stat.out2, tip.length = 0, hide.ns = TRUE, y.position = 0.4) +
  scale_y_continuous(name = "Liver br-PFOS %", labels = function(x) paste0(x*100, "%"))
}
p.stat2

suppressMessages(ggsave(filename = paste0("plots/animal_data/pfos/isomer_",OUTCOME,"_liver_feed.pdf"), plot = p.stat2, device = "pdf", dpi = 300, units = "mm", height = 80, width = 75))
suppressMessages(ggsave(filename = paste0("plots/animal_data/pfos/isomer_",OUTCOME,"_liver_feed.png"), plot = p.stat2, device = "png", dpi = 300, units = "mm", height = 80, width = 75))

p.liver1 <- p.stat
p.liver2 <- p.stat2
save(p.liver1, p.liver2, file = "plots/animal_data/pfos/liver_isomer.Rdata")

# clear the environment and release memory
rm(list = ls(all.names = TRUE)[ls(all.names = TRUE) != "params"])
invisible(gc())

7.2.2.4 Conclusion

From BL-ratio of liver, we can see a significant higher ratio in the High fibre group was observed on Day 8, while no difference but similar tendency was observed on Day 21. There was no difference between days.

7.2.3 Brain data

Investigation of bl-ratio in treatment groups in Brain tissue samples on Day 8 and 21.

7.2.3.1 Prepare data

# Load data
load("R_objects/pfos_isomer_data.Rdata")

# Subset
dat.clean <- subset(dat, material == "Brain"& !id == 133) # & !id == 133
# Sort dat.clean
dat.clean <- dat.clean[order(dat.clean$feed),]

# Set names of variables
PREDICTOR <- c("day","feed")
OUTCOME <- "br_pct" #"bl_ratio"
SUBJECT <- "rat_name"

# Create formula
PREDICTOR.F <- ifelse(length(PREDICTOR) > 1, paste(PREDICTOR, collapse = "*"), PREDICTOR)
FORMULA <- as.formula(paste(OUTCOME,PREDICTOR.F, sep = " ~ "))

# Summary samples in groups
dat.clean %>% group_by(across(all_of(PREDICTOR))) %>% get_summary_stats(!!sym(OUTCOME), type = "full") %>% select("day","feed","n","min","max","mean","sd","se")
## # A tibble: 4 × 8
##   day   feed      n   min   max  mean    sd    se
##   <chr> <chr> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
## 1 d08   HF        4 0.131 0.161 0.144 0.014 0.007
## 2 d08   LF        5 0.113 0.169 0.138 0.022 0.01 
## 3 d21   HF        5 0.14  0.273 0.197 0.051 0.023
## 4 d21   LF        6 0.149 0.279 0.187 0.047 0.019
dat.clean %>% group_by(across(all_of(PREDICTOR))) %>% get_summary_stats(!!sym("l_pct"), type = "full") %>% select("day","feed","n","min","max","mean","sd","se")
## # A tibble: 4 × 8
##   day   feed      n   min   max  mean    sd    se
##   <chr> <chr> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
## 1 d08   HF        4 0.839 0.869 0.856 0.014 0.007
## 2 d08   LF        5 0.831 0.887 0.862 0.022 0.01 
## 3 d21   HF        5 0.727 0.86  0.803 0.051 0.023
## 4 d21   LF        6 0.721 0.851 0.813 0.047 0.019
# Test for outliers
dat.clean %>% 
  group_by(across(all_of(PREDICTOR))) %>% 
  identify_outliers(!!sym(OUTCOME))
## # A tibble: 1 × 24
##   feed  day      id sample_name rat_name sample_org rat_org type  material    df
##   <chr> <chr> <int> <chr>       <chr>    <chr>      <chr>   <chr> <chr>    <int>
## 1 LF    d21     114 R20Bd21     R20      R20B21     R20     samp… Brain     8000
## # ℹ 14 more variables: batch_no <int>, notes <chr>, org_conc <dbl>,
## #   sample_conc_ug <dbl>, pfos_t_amt <dbl>, pfos_b_amt <dbl>, pfos_l_amt <dbl>,
## #   bl_ratio <dbl>, dayfeed <chr>, matfeed <chr>, br_pct <dbl>, l_pct <dbl>,
## #   is.outlier <lgl>, is.extreme <lgl>
# Check normality
# Build the linear model
model  <- lm(FORMULA, data = dat.clean)
# Create a QQ plot of residuals
ggqqplot(residuals(model))

# Compute Shapiro-Wilk test of normality
shapiro_test(residuals(model))
## # A tibble: 1 × 3
##   variable         statistic p.value
##   <chr>                <dbl>   <dbl>
## 1 residuals(model)     0.904  0.0490
# Check the homogeneity of variances with Levene's test
dat.clean %>% levene_test(FORMULA)
## # A tibble: 1 × 4
##     df1   df2 statistic     p
##   <int> <int>     <dbl> <dbl>
## 1     3    16     0.892 0.466
# Save result
EQUAL.VAR <- dat.clean %>% levene_test(FORMULA) %>% pull(p) > 0.05

This shows that data has two outlier of which one is critical and has been excluded (id 133). Data is furthermore normally distribution and has equal variance. Therefore we can test the data with a one-way ANOVA test with Tukey’s honest significance test.

7.2.3.2 ANOVA One-Way test

Perform test

If we had equality of variance we can now run a one-way ANOVA tests anova_test() (if we have equal variance) or a welch_anova_test() (if variance vary).

if(EQUAL.VAR) {
  res.aov <- dat.clean %>% anova_test(FORMULA)
  res.aov
} else {
  res.aov <- dat.clean %>% welch_anova_test(FORMULA)
  res.aov
}
## ANOVA Table (type II tests)
## 
##     Effect DFn DFd     F     p p<.05      ges
## 1      day   1  16 8.458 0.010     * 0.346000
## 2     feed   1  16 0.220 0.645       0.014000
## 3 day:feed   1  16 0.013 0.910       0.000827

Perform posthoc test

if(EQUAL.VAR) {
  pwc <- dat.clean %>% tukey_hsd(FORMULA)
  pwc
} else {
  pwc <- dat.clean %>% games_howell_test(FORMULA)
  pwc
}
## # A tibble: 8 × 9
##   term  group1 group2 null.value estimate conf.low conf.high  p.adj p.adj.signif
## * <chr> <chr>  <chr>       <dbl>    <dbl>    <dbl>     <dbl>  <dbl> <chr>       
## 1 day   d08    d21             0  0.0507    0.0138    0.0877 0.0102 *           
## 2 feed  HF     LF              0 -0.00817  -0.0451    0.0287 0.645  ns          
## 3 day:… d08:HF d21:HF          0  0.0529   -0.0215    0.127  0.217  ns          
## 4 day:… d08:HF d08:LF          0 -0.00595  -0.0803    0.0684 0.996  ns          
## 5 day:… d08:HF d21:LF          0  0.0429   -0.0287    0.114  0.349  ns          
## 6 day:… d21:HF d08:LF          0 -0.0588   -0.129     0.0113 0.117  ns          
## 7 day:… d21:HF d21:LF          0 -0.00999  -0.0771    0.0571 0.973  ns          
## 8 day:… d08:LF d21:LF          0  0.0488   -0.0183    0.116  0.201  ns

Significant impact is observed for between days but no other factors. We will plot this as a nested analysis with pairwise t-tests on the inner variable and the outer variable.

7.2.3.3 Create figure

## Pairwise comparison for inner variable: day
stat.in <- dat.clean %>%
  group_by(day) %>%
  t_test(as.formula(paste0(OUTCOME," ~ feed")), 
         paired = FALSE, var.equal = EQUAL.VAR, 
         detailed = TRUE, alternative = "two.sided") %>%
  add_significance() %>%
  p_format("p", accuracy = 0.001, trailing.zero = TRUE, new.col = TRUE)
stat.in
## # A tibble: 2 × 18
##   day   estimate estimate1 estimate2 .y.    group1 group2    n1    n2 statistic
## * <chr>    <dbl>     <dbl>     <dbl> <chr>  <chr>  <chr>  <int> <int>     <dbl>
## 1 d08    0.00595     0.144     0.138 br_pct HF     LF         4     5     0.476
## 2 d21    0.00999     0.197     0.187 br_pct HF     LF         5     6     0.337
## # ℹ 8 more variables: p <dbl>, df <dbl>, conf.low <dbl>, conf.high <dbl>,
## #   method <chr>, alternative <chr>, p.signif <chr>, p.format <chr>
## Pairwise comparison for outer variable
stat.out <- dat.clean %>%
  t_test(as.formula(paste0(OUTCOME," ~ day")),
         paired = FALSE, var.equal = EQUAL.VAR,
         detailed = TRUE, alternative = "two.sided") %>%
  add_significance() %>%
  p_format("p", accuracy = 0.001, trailing.zero = TRUE, new.col = TRUE)
stat.out
## # A tibble: 1 × 17
##   estimate estimate1 estimate2 .y.   group1 group2    n1    n2 statistic       p
## *    <dbl>     <dbl>     <dbl> <chr> <chr>  <chr>  <int> <int>     <dbl>   <dbl>
## 1  -0.0507     0.141     0.192 br_p… d08    d21        9    11     -3.07 0.00663
## # ℹ 7 more variables: df <dbl>, conf.low <dbl>, conf.high <dbl>, method <chr>,
## #   alternative <chr>, p.signif <chr>, p.format <chr>
## Calculate positions statistics on plot
stat.in <- stat.in %>% add_xy_position(x = "day", dodge = 0.8)
stat.out <- stat.out %>% add_xy_position(x = "day")
stat.out$y.position <- max(stat.in$y.position)*1.1

# Create plot
p <- ggboxplot(dat.clean, x = "day", y = OUTCOME,
               fill = "feed",
               color = "feed",
               add = "jitter",
               add.params = list(size = 1)) +
  theme_pubr(legend = "top") +
  scale_color_manual(values = c("LF" = "black","HF" = "black")) +
  scale_fill_manual(values = params$COLFEED, name = "Feed") +
  
  scale_x_discrete(name = "Day", labels = c("d08" = "Day 8","d16" = "Day 16","d21" = "Day 21")) +
  theme(axis.title.x = element_blank()) +
  guides(color = "none")

if (OUTCOME == "bl_ratio") {
p.stat <- p + stat_pvalue_manual(stat.in, tip.length = 0, hide.ns = TRUE, color = "red") +
  stat_pvalue_manual(stat.out, tip.length = 0, hide.ns = TRUE) +
  scale_y_continuous(name = "Brain B/L ratio")
} else if (OUTCOME == "br_pct") {
p.stat <- p + stat_pvalue_manual(stat.in, tip.length = 0, hide.ns = TRUE, color = "red") +
  stat_pvalue_manual(stat.out, tip.length = 0, hide.ns = TRUE) +
  scale_y_continuous(name = "Brain br-PFOS %", labels = function(x) paste0(x*100, "%"))
}
p.stat

suppressMessages(ggsave(filename = paste0("plots/animal_data/pfos/isomer_",OUTCOME,"_brain.pdf"), plot = p.stat, device = "pdf", dpi = 300, units = "mm", height = 100, width = 75))
suppressMessages(ggsave(filename = paste0("plots/animal_data/pfos/isomer_",OUTCOME,"_brain.png"), plot = p.stat, device = "png", dpi = 300, units = "mm", height = 100, width = 75))

# BETWEEN DIETS
## Pairwise comparison for inner variable: day
stat.in2 <- dat.clean %>%
  group_by(feed) %>%
  t_test(as.formula(paste0(OUTCOME," ~ day")), 
         paired = FALSE, var.equal = EQUAL.VAR, 
         detailed = TRUE, alternative = "two.sided") %>%
  add_significance() %>%
  p_format("p", accuracy = 0.001, trailing.zero = TRUE, new.col = TRUE)
stat.in2
## # A tibble: 2 × 18
##   feed  estimate estimate1 estimate2 .y.    group1 group2    n1    n2 statistic
## * <chr>    <dbl>     <dbl>     <dbl> <chr>  <chr>  <chr>  <int> <int>     <dbl>
## 1 HF     -0.0529     0.144     0.197 br_pct d08    d21        4     5     -2.00
## 2 LF     -0.0488     0.138     0.187 br_pct d08    d21        5     6     -2.11
## # ℹ 8 more variables: p <dbl>, df <dbl>, conf.low <dbl>, conf.high <dbl>,
## #   method <chr>, alternative <chr>, p.signif <chr>, p.format <chr>
## Pairwise comparison for outer variable
stat.out2 <- dat.clean %>%
  t_test(as.formula(paste0(OUTCOME," ~ feed")),
         paired = FALSE, var.equal = EQUAL.VAR,
         detailed = TRUE, alternative = "two.sided") %>%
  add_significance() %>%
  p_format("p", accuracy = 0.001, trailing.zero = TRUE, new.col = TRUE)
stat.out2
## # A tibble: 1 × 17
##   estimate estimate1 estimate2 .y.    group1 group2    n1    n2 statistic     p
## *    <dbl>     <dbl>     <dbl> <chr>  <chr>  <chr>  <int> <int>     <dbl> <dbl>
## 1  0.00869     0.174     0.165 br_pct HF     LF         9    11     0.428 0.674
## # ℹ 7 more variables: df <dbl>, conf.low <dbl>, conf.high <dbl>, method <chr>,
## #   alternative <chr>, p.signif <chr>, p.format <chr>
## Calculate positions statistics on plot
stat.in2 <- stat.in2 %>% add_xy_position(x = "feed", dodge = 0.8)
stat.out2 <- stat.out2 %>% add_xy_position(x = "feed")
stat.out2$y.position <- max(stat.in2$y.position)*1.1

# Create plot
p <- ggboxplot(dat.clean, x = "feed", y = OUTCOME,
               fill = "feed",
               color = "day",
               shape = "day",
               add = "jitter",
               add.params = list(size = 1, position_jitterdodge(dodge.width = 0.8))) +
  theme_pubr() + 
  scale_color_manual(values = params$COL_4BLACK) +#, name = "Day", labels = c("d08" = "8", "d12" = "12", "d16" = "16","d21" = "21")) +
  scale_fill_manual(values = params$COLFEED, name = "Feed") +
  scale_shape_manual(values = c("d08" = 19, "d12" = 1, "d16" = 17, "d21" = 2), name = "Day", labels = c("d08" = "8", "d12" = "12", "d16" = "16","d21" = "21")) +
  scale_x_discrete(name = "Feed") +
  theme(axis.title.x = element_blank()) +
  guides(color = "none", fill = "none", shape = "none")

if (OUTCOME == "bl_ratio") {
p.stat2 <- p + stat_pvalue_manual(stat.in2, tip.length = 0, hide.ns = TRUE, color = "red") +
  stat_pvalue_manual(stat.out2, tip.length = 0, hide.ns = TRUE) +
  scale_y_continuous(name = "Brain B/L ratio")
} else if (OUTCOME == "br_pct") {
p.stat2 <- p + stat_pvalue_manual(stat.in2, tip.length = 0, hide.ns = TRUE, color = "red") +
  stat_pvalue_manual(stat.out2, tip.length = 0, hide.ns = TRUE) +
  scale_y_continuous(name = "Brain br-PFOS %", labels = function(x) paste0(x*100, "%"))
}
p.stat2

suppressMessages(ggsave(filename = paste0("plots/animal_data/pfos/isomer_",OUTCOME,"_brain_feed.pdf"), plot = p.stat2, device = "pdf", dpi = 300, units = "mm", height = 80, width = 75))
suppressMessages(ggsave(filename = paste0("plots/animal_data/pfos/isomer_",OUTCOME,"_brain_feed.png"), plot = p.stat2, device = "png", dpi = 300, units = "mm", height = 80, width = 75))

p.brain1 <- p.stat
p.brain2 <- p.stat2
save(p.brain1, p.brain2, file = "plots/animal_data/pfos/brain_isomer.Rdata")

# clear the environment and release memory
rm(list = ls(all.names = TRUE)[ls(all.names = TRUE) != "params"])
invisible(gc())

7.2.3.4 Conclusion

From BL-ratio of brain, we can see a significant higher ratio in samples on DAy 21 compared to Day 8, while no significant difference is seen between feeding groups.

7.3 EXCRETED MEASUREMENTS

7.3.1 Feces data

Investigation of bl-ratio in treatment groups in feces samples collected on Day 8, Day 12, Day 16 and Day 21.

7.3.1.1 Prepare data

# Load data
load("R_objects/pfos_isomer_data.Rdata")

# Subset
dat.clean <- subset(dat, material == "Feces" & !id == c(474))
# Sort dat.clean
dat.clean <- dat.clean[order(dat.clean$feed),]

# Set names of variables
PREDICTOR <- "dayfeed"#c("day","feed")
OUTCOME <- "br_pct" #"bl_ratio"
SUBJECT <- "rat_name"

# Create formula
PREDICTOR.F <- ifelse(length(PREDICTOR) > 1, paste(PREDICTOR, collapse = "*"), PREDICTOR)
FORMULA <- as.formula(paste(OUTCOME,PREDICTOR.F, sep = " ~ "))

# Summary samples in groups
dat.clean %>% group_by(across(all_of(c("day","feed")))) %>% get_summary_stats(!!sym(OUTCOME), type = "full") %>% select("day","feed","n","min","max","mean","sd","se")
## # A tibble: 8 × 8
##   day   feed      n   min   max  mean    sd    se
##   <chr> <chr> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
## 1 d08   HF       12 0.15  0.212 0.193 0.016 0.005
## 2 d08   LF       12 0.174 0.211 0.194 0.01  0.003
## 3 d12   HF        6 0.201 0.235 0.217 0.011 0.005
## 4 d12   LF        6 0.171 0.235 0.208 0.024 0.01 
## 5 d16   HF        6 0.195 0.243 0.214 0.016 0.006
## 6 d16   LF        6 0.163 0.205 0.191 0.015 0.006
## 7 d21   HF        5 0.2   0.246 0.219 0.017 0.008
## 8 d21   LF        6 0.203 0.25  0.226 0.019 0.008
dat.clean %>% group_by(across(all_of(c("day","feed")))) %>% get_summary_stats(!!sym("l_pct"), type = "full") %>% select("day","feed","n","min","max","mean","sd","se")
## # A tibble: 8 × 8
##   day   feed      n   min   max  mean    sd    se
##   <chr> <chr> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
## 1 d08   HF       12 0.788 0.85  0.807 0.016 0.005
## 2 d08   LF       12 0.789 0.826 0.806 0.01  0.003
## 3 d12   HF        6 0.765 0.799 0.783 0.011 0.005
## 4 d12   LF        6 0.765 0.829 0.792 0.024 0.01 
## 5 d16   HF        6 0.757 0.805 0.786 0.016 0.006
## 6 d16   LF        6 0.795 0.837 0.809 0.015 0.006
## 7 d21   HF        5 0.754 0.8   0.781 0.017 0.008
## 8 d21   LF        6 0.75  0.797 0.774 0.019 0.008
# Test for outliers
dat.clean %>% 
  group_by(across(all_of(PREDICTOR))) %>% 
  identify_outliers(!!sym(OUTCOME))
## # A tibble: 7 × 24
##   dayfeed    id sample_name rat_name sample_org rat_org type   feed  material
##   <chr>   <int> <chr>       <chr>    <chr>      <chr>   <chr>  <chr> <chr>   
## 1 d08_HF    399 R45Fd08     R45      R45F8      R45     sample HF    Feces   
## 2 d12_HF    422 R44Fd12     R44      R42F12     R42     sample HF    Feces   
## 3 d12_HF    426 R48Fd12     R48      R48F12     R48     sample HF    Feces   
## 4 d16_HF    445 R43Fd16     R43      R41F16     R41     sample HF    Feces   
## 5 d16_HF    446 R44Fd16     R44      R42F16     R42     sample HF    Feces   
## 6 d16_LF    433 R19Fd16     R19      R19F16     R19     sample LF    Feces   
## 7 d21_HF    470 R44Fd21     R44      R42F21     R42     sample HF    Feces   
## # ℹ 15 more variables: day <chr>, df <int>, batch_no <int>, notes <chr>,
## #   org_conc <dbl>, sample_conc_ug <dbl>, pfos_t_amt <dbl>, pfos_b_amt <dbl>,
## #   pfos_l_amt <dbl>, bl_ratio <dbl>, matfeed <chr>, br_pct <dbl>, l_pct <dbl>,
## #   is.outlier <lgl>, is.extreme <lgl>
# Check normality
# Build the linear model
model  <- lm(FORMULA, data = dat.clean)
# Create a QQ plot of residuals
ggqqplot(residuals(model))

# Compute Shapiro-Wilk test of normality
shapiro_test(residuals(model))
## # A tibble: 1 × 3
##   variable         statistic p.value
##   <chr>                <dbl>   <dbl>
## 1 residuals(model)     0.975   0.259
# Check the homogeneity of variances with Levene's test
dat.clean %>% levene_test(FORMULA)
## Warning in leveneTest.default(y = y, group = group, ...): group coerced to
## factor.
## # A tibble: 1 × 4
##     df1   df2 statistic     p
##   <int> <int>     <dbl> <dbl>
## 1     7    51      1.12 0.363
# Save result
EQUAL.VAR <- dat.clean %>% levene_test(FORMULA) %>% pull(p) > 0.05
## Warning in leveneTest.default(y = y, group = group, ...): group coerced to
## factor.

This shows that data has seven outliers, is not normally distribution and has equal variance. Therefore we can test the data with a Kruskal-Wallis with posthoc Dunn’s test adjusted p-values.

7.3.1.2 Kruskal-Wallis test

Perform test

res.aov <- dat.clean %>% kruskal_test(FORMULA)
res.aov
## # A tibble: 1 × 6
##   .y.        n statistic    df        p method        
## * <chr>  <int>     <dbl> <int>    <dbl> <chr>         
## 1 br_pct    59      28.2     7 0.000202 Kruskal-Wallis

Effect size

The eta squared, based on the H-statistic, can be used as the measure of the Kruskal-Wallis test effect size. The interpretation values commonly in published literature are: 0.01- < 0.06 (small effect), 0.06 - < 0.14 (moderate effect) and >= 0.14 (large effect).

dat.clean %>% kruskal_effsize(FORMULA)
## # A tibble: 1 × 5
##   .y.        n effsize method  magnitude
## * <chr>  <int>   <dbl> <chr>   <ord>    
## 1 br_pct    59   0.416 eta2[H] large

Post-hoc test if interaction is significant

A significant Kruskal-Wallis test is generally followed up by Dunn’s test to identify which groups are different. It’s also possible to use the Wilcoxon’s test to calculate pairwise comparisons between group levels with corrections for multiple testing.

# pairwise comparisons
pwc <- dat.clean %>% 
  dunn_test(FORMULA, p.adjust.method = "fdr") 
pwc
## # A tibble: 28 × 9
##    .y.    group1 group2    n1    n2 statistic       p  p.adj p.adj.signif
##  * <chr>  <chr>  <chr>  <int> <int>     <dbl>   <dbl>  <dbl> <chr>       
##  1 br_pct d08_HF d08_LF    12    12    -0.131 0.896   0.968  ns          
##  2 br_pct d08_HF d12_HF    12     6     2.93  0.00338 0.0190 *           
##  3 br_pct d08_HF d12_LF    12     6     1.51  0.130   0.243  ns          
##  4 br_pct d08_HF d16_HF    12     6     2.27  0.0232  0.0590 ns          
##  5 br_pct d08_HF d16_LF    12     6    -0.116 0.907   0.968  ns          
##  6 br_pct d08_HF d21_HF    12     5     2.66  0.00786 0.0291 *           
##  7 br_pct d08_HF d21_LF    12     6     3.28  0.00104 0.0145 *           
##  8 br_pct d08_LF d12_HF    12     6     3.04  0.00239 0.0190 *           
##  9 br_pct d08_LF d12_LF    12     6     1.62  0.105   0.226  ns          
## 10 br_pct d08_LF d16_HF    12     6     2.38  0.0174  0.0488 *           
## # ℹ 18 more rows

Several groups show significant difference based of day and feed. We will plot this as a nested analysis with feed as the inner variable using a t-test and day as the outer variable using kruskal-wallis with posthoc Dunn’s test.

7.3.1.3 Create figure

## Pairwise comparison for inner variable: day
stat.in <- dat.clean %>%
  group_by(day) %>%
  wilcox_test(as.formula(paste0(OUTCOME," ~ feed"))) %>%
  adjust_pvalue(method = "BH") %>%
  add_significance("p.adj") %>% 
  add_xy_position(x = "day", dodge = 0.8) %>%
  p_format("p.adj", accuracy = 0.0001, trailing.zero = TRUE, new.col = TRUE)
stat.in
## # A tibble: 4 × 16
##   day   .y.    group1 group2    n1    n2 statistic      p  p.adj p.adj.signif
## * <chr> <chr>  <chr>  <chr>  <int> <int>     <dbl>  <dbl>  <dbl> <chr>       
## 1 d08   br_pct HF     LF        12    12        75 0.887  0.887  ns          
## 2 d12   br_pct HF     LF         6     6        22 0.589  0.883  ns          
## 3 d16   br_pct HF     LF         6     6        33 0.0152 0.0608 ns          
## 4 d21   br_pct HF     LF         5     6        12 0.662  0.883  ns          
## # ℹ 6 more variables: y.position <dbl>, groups <named list>, x <dbl>,
## #   xmin <dbl>, xmax <dbl>, p.adj.format <chr>
## Pairwise comparison for outer variable
stat.out <- dat.clean %>%
  kruskal_test(as.formula(paste0(OUTCOME," ~ day"))) %>%
  add_significance() %>%
  p_format("p", accuracy = 0.001, trailing.zero = TRUE, new.col = TRUE)
stat.out
## # A tibble: 1 × 8
##   .y.        n statistic    df         p method         p.signif p.format
## * <chr>  <int>     <dbl> <int>     <dbl> <chr>          <chr>    <chr>   
## 1 br_pct    59      22.3     3 0.0000574 Kruskal-Wallis ****     <0.001
pwc2 <- dat.clean %>%
  dunn_test(as.formula(paste0(OUTCOME," ~ day"))) %>%
  add_significance() %>%
  p_format("p.adj", accuracy = 0.001, trailing.zero = TRUE, new.col = TRUE)
pwc2
## # A tibble: 6 × 10
##   .y.    group1 group2    n1    n2 statistic         p    p.adj p.adj.signif
## * <chr>  <chr>  <chr>  <int> <int>     <dbl>     <dbl>    <dbl> <chr>       
## 1 br_pct d08    d12       24    12      3.22 0.00129   0.00645  **          
## 2 br_pct d08    d16       24    12      1.60 0.110     0.330    ns          
## 3 br_pct d08    d21       24    11      4.30 0.0000174 0.000104 ***         
## 4 br_pct d12    d16       12    12     -1.40 0.161     0.330    ns          
## 5 br_pct d12    d21       12    11      1.02 0.307     0.330    ns          
## 6 br_pct d16    d21       12    11      2.39 0.0167    0.0668   ns          
## # ℹ 1 more variable: p.adj.format <chr>
## Calculate positions statistics on plot
pwc2 <- pwc2 %>% add_xy_position(x = "day")
pwc2$y.position <- max(stat.in$y.position)*1.1

# Create plot
p <- ggboxplot(dat.clean, x = "day", y = OUTCOME,
               fill = "feed",
               color = "feed",
               add = "jitter",
               add.params = list(size = 1)) +
  theme_pubr(legend = "top") +
  scale_color_manual(values = c("LF" = "black","HF" = "black")) +
  scale_fill_manual(values = params$COLFEED, name = "Feed") +
  scale_x_discrete(name = "Day", labels = c("d08" = "Day 8","d12" = "Day 12","d16" = "Day 16","d21" = "Day 21")) +
  theme(axis.title.x = element_blank()) +
  guides(color = "none")

if (OUTCOME == "bl_ratio") {
p.stat <- p + stat_pvalue_manual(stat.in, tip.length = 0, hide.ns = TRUE, color = "red") +
  stat_pvalue_manual(pwc2, tip.length = 0, hide.ns = TRUE, y.position = c(0.33, 0.34)) +
  scale_y_continuous(name = "Faeces B/L ratio")
} else if (OUTCOME == "br_pct") {
p.stat <- p + stat_pvalue_manual(stat.in, tip.length = 0, hide.ns = TRUE, color = "red") +
  stat_pvalue_manual(pwc2, tip.length = 0, hide.ns = TRUE, y.position = c(0.26, 0.27)) +
  scale_y_continuous(name = "Faeces br-PFOS %", labels = function(x) paste0(x*100, "%"))
}
p.stat

suppressMessages(ggsave(filename = paste0("plots/animal_data/pfos/isomer_",OUTCOME,"_feces.pdf"), plot = p.stat, device = "pdf", dpi = 300, units = "mm", height = 100, width = 125))
suppressMessages(ggsave(filename = paste0("plots/animal_data/pfos/isomer_",OUTCOME,"_feces.png"), plot = p.stat, device = "png", dpi = 300, units = "mm", height = 100, width = 125))

# BETWEEN DIETS
## Pairwise comparison for inner variable: day
stat.in2 <- dat.clean %>%
  group_by(feed) %>%
  kruskal_test(as.formula(paste0(OUTCOME," ~ day"))) %>%
  add_significance() %>%
  p_format("p", accuracy = 0.001, trailing.zero = TRUE, new.col = TRUE)
stat.in2
## # A tibble: 2 × 9
##   feed  .y.        n statistic    df       p method         p.signif p.format
## * <chr> <chr>  <int>     <dbl> <int>   <dbl> <chr>          <chr>    <chr>   
## 1 HF    br_pct    29      15.0     3 0.00184 Kruskal-Wallis **       0.0018  
## 2 LF    br_pct    30      11.6     3 0.00882 Kruskal-Wallis **       0.0088
pwc.in2 <- dat.clean %>%
  group_by(feed) %>%
  dunn_test(as.formula(paste0(OUTCOME," ~ day"))) %>%
  add_significance() %>%
  p_format("p.adj", accuracy = 0.001, trailing.zero = TRUE, new.col = TRUE)
pwc.in2
## # A tibble: 12 × 11
##    feed  .y.    group1 group2    n1    n2 statistic       p   p.adj p.adj.signif
##  * <chr> <chr>  <chr>  <chr>  <int> <int>     <dbl>   <dbl>   <dbl> <chr>       
##  1 HF    br_pct d08    d12       12     6    3.19   0.00142 0.00852 **          
##  2 HF    br_pct d08    d16       12     6    2.25   0.0244  0.0975  ns          
##  3 HF    br_pct d08    d21       12     5    2.93   0.00338 0.0169  *           
##  4 HF    br_pct d12    d16        6     6   -0.814  0.416   1       ns          
##  5 HF    br_pct d12    d21        6     5   -0.0582 0.954   1       ns          
##  6 HF    br_pct d16    d21        6     5    0.718  0.473   1       ns          
##  7 LF    br_pct d08    d12       12     6    1.33   0.185   0.555   ns          
##  8 LF    br_pct d08    d16       12     6    0.0379 0.970   0.970   ns          
##  9 LF    br_pct d08    d21       12     6    3.18   0.00147 0.00882 **          
## 10 LF    br_pct d12    d16        6     6   -1.11   0.265   0.555   ns          
## 11 LF    br_pct d12    d21        6     6    1.61   0.108   0.432   ns          
## 12 LF    br_pct d16    d21        6     6    2.72   0.00650 0.0325  *           
## # ℹ 1 more variable: p.adj.format <chr>
## Pairwise comparison for outer variable
stat.out2 <- dat.clean %>%
  wilcox_test(as.formula(paste0(OUTCOME," ~ feed"))) %>%
  adjust_pvalue(method = "BH") %>%
  add_significance("p.adj") %>% 
  add_xy_position(x = "day", dodge = 0.8) %>%
  p_format("p.adj", accuracy = 0.0001, trailing.zero = TRUE, new.col = TRUE)
stat.out2
## # A tibble: 1 × 14
##   .y.    group1 group2    n1    n2 statistic     p p.adj p.adj.signif y.position
## * <chr>  <chr>  <chr>  <int> <int>     <dbl> <dbl> <dbl> <chr>             <dbl>
## 1 br_pct HF     LF        29    30       520 0.202 0.202 ns                0.250
## # ℹ 4 more variables: groups <named list>, xmin <dbl>, xmax <dbl>,
## #   p.adj.format <chr>
## Calculate positions statistics on plot
pwc.in2 <- pwc.in2 %>% add_xy_position(x = "feed", dodge = 0.8)

# Create plot
p <- ggboxplot(dat.clean, x = "feed", y = OUTCOME,
               fill = "feed",
               color = "day",
               shape = "day",
               add = "jitter",
               add.params = list(size = 1, position_jitterdodge(dodge.width = 0.8))) +
  theme_pubr() + 
  scale_color_manual(values = params$COL_4BLACK) +
  scale_fill_manual(values = params$COLFEED, name = "Feed") +
  scale_shape_manual(values = c("d08" = 19, "d12" = 1, "d16" = 17, "d21" = 2), name = "Day", labels = c("d08" = "8", "d12" = "12", "d16" = "16","d21" = "21")) +
  scale_x_discrete(name = "Feed") +
  theme(axis.title.x = element_blank()) +
  guides(color = "none", fill = "none", shape = "none")

if (OUTCOME == "bl_ratio") {
p.stat2 <- p + stat_pvalue_manual(pwc.in2, tip.length = 0, hide.ns = TRUE, color = "red") +
  stat_pvalue_manual(stat.out2, tip.length = 0, hide.ns = TRUE) +
  scale_y_continuous(name = "Faeces B/L ratio")
} else if (OUTCOME == "br_pct") {
  p.stat2 <- p + stat_pvalue_manual(pwc.in2, tip.length = 0, hide.ns = TRUE, color = "red", y.position = c(0.245,0.255,0.255,0.265)) +
  stat_pvalue_manual(stat.out2, tip.length = 0, hide.ns = TRUE) +
  scale_y_continuous(name = "Faeces br-PFOS %", labels = function(x) paste0(x*100, "%"))
}
p.stat2

suppressMessages(ggsave(filename = paste0("plots/animal_data/pfos/isomer_",OUTCOME,"_faeces_feed.pdf"), plot = p.stat2, device = "pdf", dpi = 300, units = "mm", height = 80, width = 125))
suppressMessages(ggsave(filename = paste0("plots/animal_data/pfos/isomer_",OUTCOME,"_faeces_feed.png"), plot = p.stat2, device = "png", dpi = 300, units = "mm", height = 80, width = 125))

p.faeces1 <- p.stat
p.faeces2 <- p.stat2
save(p.faeces1, p.faeces2, file = "plots/animal_data/pfos/faeces_isomer.Rdata")

# clear the environment and release memory
rm(list = ls(all.names = TRUE)[ls(all.names = TRUE) != "params"])
invisible(gc())

7.3.1.4 Conclusion

From BL-ratio of feces, we can see that a significant difference was observed between Day 8 and Day 21, as well as between feed groups on day 16. Results indicate that BL-ratio increase over time for feces excreted PFOS.

7.3.2 Cecum data

Investigation of bl-ratio in treatment groups in caecal content samples on Day 8 and Day 21.

7.3.2.1 Prepare data

# Load data
load("R_objects/pfos_isomer_data.Rdata")

# Subset
dat.clean <- subset(dat, material == "Cecum") # & !id == 133
# Sort dat.clean
dat.clean <- dat.clean[order(dat.clean$feed),]

# Set names of variables
PREDICTOR <- "dayfeed"#c("day","feed")
OUTCOME <- "br_pct" #"bl_ratio"
SUBJECT <- "rat_name"

# Create formula
PREDICTOR.F <- ifelse(length(PREDICTOR) > 1, paste(PREDICTOR, collapse = "*"), PREDICTOR)
FORMULA <- as.formula(paste(OUTCOME,PREDICTOR.F, sep = " ~ "))

# Summary samples in groups
dat.clean %>% group_by(across(all_of(c("day","feed")))) %>% get_summary_stats(!!sym(OUTCOME), type = "full") %>% select("day","feed","n","min","max","mean","sd","se")
## # A tibble: 4 × 8
##   day   feed      n   min   max  mean    sd    se
##   <chr> <chr> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
## 1 d08   HF        6 0.187 0.221 0.201 0.012 0.005
## 2 d08   LF        6 0.171 0.202 0.186 0.014 0.006
## 3 d21   HF        6 0.179 0.222 0.195 0.015 0.006
## 4 d21   LF        6 0.124 0.219 0.176 0.035 0.014
dat.clean %>% group_by(across(all_of(c("day","feed")))) %>% get_summary_stats(!!sym("l_pct"), type = "full") %>% select("day","feed","n","min","max","mean","sd","se")
## # A tibble: 4 × 8
##   day   feed      n   min   max  mean    sd    se
##   <chr> <chr> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
## 1 d08   HF        6 0.779 0.813 0.799 0.012 0.005
## 2 d08   LF        6 0.798 0.829 0.814 0.014 0.006
## 3 d21   HF        6 0.778 0.821 0.805 0.015 0.006
## 4 d21   LF        6 0.781 0.876 0.824 0.035 0.014
# Test for outliers
dat.clean %>% 
  group_by(across(all_of(PREDICTOR))) %>% 
  identify_outliers(!!sym(OUTCOME))
## # A tibble: 2 × 24
##   dayfeed    id sample_name rat_name sample_org rat_org type   feed  material
##   <chr>   <int> <chr>       <chr>    <chr>      <chr>   <chr>  <chr> <chr>   
## 1 d08_HF    333 R41Cd08     R41      R43C8      R43     sample HF    Cecum   
## 2 d21_HF    332 R44Cd21     R44      R42C21     R42     sample HF    Cecum   
## # ℹ 15 more variables: day <chr>, df <int>, batch_no <int>, notes <chr>,
## #   org_conc <dbl>, sample_conc_ug <dbl>, pfos_t_amt <dbl>, pfos_b_amt <dbl>,
## #   pfos_l_amt <dbl>, bl_ratio <dbl>, matfeed <chr>, br_pct <dbl>, l_pct <dbl>,
## #   is.outlier <lgl>, is.extreme <lgl>
# Check normality
# Build the linear model
model  <- lm(FORMULA, data = dat.clean)
# Create a QQ plot of residuals
ggqqplot(residuals(model))

# Compute Shapiro-Wilk test of normality
shapiro_test(residuals(model))
## # A tibble: 1 × 3
##   variable         statistic p.value
##   <chr>                <dbl>   <dbl>
## 1 residuals(model)     0.954   0.334
# Check the homogeneity of variances with Levene's test
dat.clean %>% levene_test(FORMULA)
## Warning in leveneTest.default(y = y, group = group, ...): group coerced to
## factor.
## # A tibble: 1 × 4
##     df1   df2 statistic      p
##   <int> <int>     <dbl>  <dbl>
## 1     3    20      3.39 0.0381
# Save result
EQUAL.VAR <- dat.clean %>% levene_test(FORMULA) %>% pull(p) > 0.05
## Warning in leveneTest.default(y = y, group = group, ...): group coerced to
## factor.

This shows that data has no outlier, normal distribution, but not equal variance. Therefore we can test the data with a Welch ANOVA test with Games Howell test.

7.3.2.2 Welch ANOVA One-Way test

Perform test

If we had equality of variance we can now run a one-way ANOVA tests anova_test() (if we have equal variance) or a welch_anova_test() (if variance vary).

if(EQUAL.VAR) {
  res.aov <- dat.clean %>% anova_test(FORMULA)
  res.aov
} else {
  res.aov <- dat.clean %>% welch_anova_test(FORMULA)
  res.aov
}
## # A tibble: 1 × 7
##   .y.        n statistic   DFn   DFd     p method     
## * <chr>  <int>     <dbl> <dbl> <dbl> <dbl> <chr>      
## 1 br_pct    24      1.55     3  10.8 0.257 Welch ANOVA

Perform posthoc test

if(EQUAL.VAR) {
  pwc <- dat.clean %>% tukey_hsd(FORMULA)
  pwc
} else {
  pwc <- dat.clean %>% games_howell_test(FORMULA)
  pwc
}
## # A tibble: 6 × 8
##   .y.    group1 group2 estimate conf.low conf.high p.adj p.adj.signif
## * <chr>  <chr>  <chr>     <dbl>    <dbl>     <dbl> <dbl> <chr>       
## 1 br_pct d08_HF d08_LF -0.0143   -0.0374   0.00870 0.284 ns          
## 2 br_pct d08_HF d21_HF -0.00588  -0.0305   0.0188  0.879 ns          
## 3 br_pct d08_HF d21_LF -0.0242   -0.0756   0.0272  0.436 ns          
## 4 br_pct d08_LF d21_HF  0.00845  -0.0177   0.0346  0.758 ns          
## 5 br_pct d08_LF d21_LF -0.00985  -0.0613   0.0416  0.914 ns          
## 6 br_pct d21_HF d21_LF -0.0183   -0.0698   0.0332  0.657 ns

No significant impact is observed. As data was normally distributed we will plot this as a nested analysis with pairwise t-tests on the inner variable and the outer variable.

7.3.2.3 Create figure

## Pairwise comparison for inner variable: day
stat.in <- dat.clean %>%
  group_by(day) %>%
  t_test(as.formula(paste0(OUTCOME," ~ feed")), 
         paired = FALSE, var.equal = EQUAL.VAR, 
         detailed = TRUE, alternative = "two.sided") %>%
  add_significance() %>%
  p_format("p", accuracy = 0.001, trailing.zero = TRUE, new.col = TRUE)
stat.in
## # A tibble: 2 × 18
##   day   estimate estimate1 estimate2 .y.    group1 group2    n1    n2 statistic
## * <chr>    <dbl>     <dbl>     <dbl> <chr>  <chr>  <chr>  <int> <int>     <dbl>
## 1 d08     0.0143     0.201     0.186 br_pct HF     LF         6     6      1.91
## 2 d21     0.0183     0.195     0.176 br_pct HF     LF         6     6      1.18
## # ℹ 8 more variables: p <dbl>, df <dbl>, conf.low <dbl>, conf.high <dbl>,
## #   method <chr>, alternative <chr>, p.signif <chr>, p.format <chr>
## Pairwise comparison for outer variable
stat.out <- dat.clean %>%
  t_test(as.formula(paste0(OUTCOME," ~ day")),
         paired = FALSE, var.equal = EQUAL.VAR,
         detailed = TRUE, alternative = "two.sided") %>%
  add_significance() %>%
  p_format("p", accuracy = 0.001, trailing.zero = TRUE, new.col = TRUE)
stat.out
## # A tibble: 1 × 17
##   estimate estimate1 estimate2 .y.    group1 group2    n1    n2 statistic     p
## *    <dbl>     <dbl>     <dbl> <chr>  <chr>  <chr>  <int> <int>     <dbl> <dbl>
## 1  0.00787     0.193     0.186 br_pct d08    d21       12    12     0.881 0.391
## # ℹ 7 more variables: df <dbl>, conf.low <dbl>, conf.high <dbl>, method <chr>,
## #   alternative <chr>, p.signif <chr>, p.format <chr>
## Calculate positions statistics on plot
stat.in <- stat.in %>% add_xy_position(x = "day", dodge = 0.8)
stat.out <- stat.out %>% add_xy_position(x = "day")
stat.out$y.position <- max(stat.in$y.position)*1.1

# Create plot
p <- ggboxplot(dat.clean, x = "day", y = OUTCOME,
               fill = "feed",
               color = "feed",
               add = "jitter",
               add.params = list(size = 1)) +
  theme_pubr(legend = "top") +
  scale_color_manual(values = c("LF" = "black","HF" = "black")) +
  scale_fill_manual(values = params$COLFEED, name = "Feed") +
  scale_x_discrete(name = "Day", labels = c("d08" = "Day 8","d16" = "Day 16","d21" = "Day 21")) +
  theme(axis.title.x = element_blank()) +
  guides(color = "none")

if (OUTCOME == "bl_ratio") {
p.stat <- p + stat_pvalue_manual(stat.in, tip.length = 0, hide.ns = TRUE, color = "red") +
  stat_pvalue_manual(stat.out, tip.length = 0, hide.ns = TRUE) +
  scale_y_continuous(name = "Caecal B/L ratio")
} else if (OUTCOME == "br_pct") {
  p.stat <- p + stat_pvalue_manual(stat.in, tip.length = 0, hide.ns = TRUE, color = "red") +
  stat_pvalue_manual(stat.out, tip.length = 0, hide.ns = TRUE) +
  scale_y_continuous(name = "Caecal br-PFOS %", labels = function(x) paste0(x*100, "%"))
}
p.stat

suppressMessages(ggsave(filename = paste0("plots/animal_data/pfos/isomer_",OUTCOME,"_cecum.pdf"), plot = p.stat, device = "pdf", dpi = 300, units = "mm", height = 100, width = 75))
suppressMessages(ggsave(filename = paste0("plots/animal_data/pfos/isomer_",OUTCOME,"_cecum.png"), plot = p.stat, device = "png", dpi = 300, units = "mm", height = 100, width = 75))

# BETWEEN DIETS
## Pairwise comparison for inner variable: day
stat.in2 <- dat.clean %>%
  group_by(feed) %>%
  t_test(as.formula(paste0(OUTCOME," ~ day")), 
         paired = FALSE, var.equal = EQUAL.VAR, 
         detailed = TRUE, alternative = "two.sided") %>%
  add_significance() %>%
  p_format("p", accuracy = 0.001, trailing.zero = TRUE, new.col = TRUE)
stat.in2
## # A tibble: 2 × 18
##   feed  estimate estimate1 estimate2 .y.    group1 group2    n1    n2 statistic
## * <chr>    <dbl>     <dbl>     <dbl> <chr>  <chr>  <chr>  <int> <int>     <dbl>
## 1 HF     0.00588     0.201     0.195 br_pct d08    d21        6     6     0.739
## 2 LF     0.00985     0.186     0.176 br_pct d08    d21        6     6     0.645
## # ℹ 8 more variables: p <dbl>, df <dbl>, conf.low <dbl>, conf.high <dbl>,
## #   method <chr>, alternative <chr>, p.signif <chr>, p.format <chr>
## Pairwise comparison for outer variable
stat.out2 <- dat.clean %>%
  t_test(as.formula(paste0(OUTCOME," ~ feed")),
         paired = FALSE, var.equal = EQUAL.VAR,
         detailed = TRUE, alternative = "two.sided") %>%
  add_significance() %>%
  p_format("p", accuracy = 0.001, trailing.zero = TRUE, new.col = TRUE)
stat.out2
## # A tibble: 1 × 17
##   estimate estimate1 estimate2 .y.    group1 group2    n1    n2 statistic      p
## *    <dbl>     <dbl>     <dbl> <chr>  <chr>  <chr>  <int> <int>     <dbl>  <dbl>
## 1   0.0163     0.198     0.181 br_pct HF     LF        12    12      1.94 0.0691
## # ℹ 7 more variables: df <dbl>, conf.low <dbl>, conf.high <dbl>, method <chr>,
## #   alternative <chr>, p.signif <chr>, p.format <chr>
## Calculate positions statistics on plot
stat.in2 <- stat.in2 %>% add_xy_position(x = "feed", dodge = 0.8)
stat.out2 <- stat.out2 %>% add_xy_position(x = "feed")
stat.out2$y.position <- max(stat.in2$y.position)*1.1

# Create plot
p <- ggboxplot(dat.clean, x = "feed", y = OUTCOME,
               fill = "feed",
               color = "day",
               shape = "day",
               add = "jitter",
               add.params = list(size = 1, position_jitterdodge(dodge.width = 0.8))) +
  theme_pubr() + 
  scale_color_manual(values = params$COL_4BLACK) +#, name = "Day", labels = c("d08" = "8", "d12" = "12", "d16" = "16","d21" = "21")) +
  scale_fill_manual(values = params$COLFEED, name = "Feed") +
  scale_shape_manual(values = c("d08" = 19, "d12" = 1, "d16" = 17, "d21" = 2), name = "Day", labels = c("d08" = "8", "d12" = "12", "d16" = "16","d21" = "21")) +
  scale_x_discrete(name = "Feed") +
  theme(axis.title.x = element_blank()) +
  guides(color = "none", fill = "none", shape = "none")

if (OUTCOME == "bl_ratio") {
p.stat2 <- p + stat_pvalue_manual(stat.in2, tip.length = 0, hide.ns = TRUE, color = "red") +
  stat_pvalue_manual(stat.out2, tip.length = 0, hide.ns = TRUE) +
  scale_y_continuous(name = "Caecal B/L ratio")
} else if (OUTCOME == "br_pct") {
p.stat2 <- p + stat_pvalue_manual(stat.in2, tip.length = 0, hide.ns = TRUE, color = "red") +
  stat_pvalue_manual(stat.out2, tip.length = 0, hide.ns = TRUE) +
  scale_y_continuous(name = "Caecal br-PFOS %", labels = function(x) paste0(x*100, "%"))
}
p.stat2

suppressMessages(ggsave(filename = paste0("plots/animal_data/pfos/isomer_",OUTCOME,"_cecum_feed.pdf"), plot = p.stat2, device = "pdf", dpi = 300, units = "mm", height = 80, width = 75))
suppressMessages(ggsave(filename = paste0("plots/animal_data/pfos/isomer_",OUTCOME,"_cecum_feed.png"), plot = p.stat2, device = "png", dpi = 300, units = "mm", height = 80, width = 75))

p.caecum1 <- p.stat
p.caecum2 <- p.stat2
save(p.caecum1, p.caecum2, file = "plots/animal_data/pfos/caecum_isomer.Rdata")

# clear the environment and release memory
rm(list = ls(all.names = TRUE)[ls(all.names = TRUE) != "params"])
invisible(gc())

7.3.2.4 Conclusion

From BL-ratio of caecal content, we see no significant difference in either day or feed groups.

7.3.3 Urine data

Investigation of bl-ratio in treatment groups in urine samples collected on Day 8, Day 16 and Day 21.

7.3.3.1 Prepare data

# Load data
load("R_objects/pfos_isomer_data.Rdata")

# Subset
dat.clean <- subset(dat, material == "Urine")# & !id == c(474))
# Sort dat.clean
dat.clean <- dat.clean[order(dat.clean$feed),]

# Set names of variables
PREDICTOR <- c("day","feed")
OUTCOME <- "br_pct" #"bl_ratio"
SUBJECT <- "rat_name"

# Create formula
PREDICTOR.F <- ifelse(length(PREDICTOR) > 1, paste(PREDICTOR, collapse = "*"), PREDICTOR)
FORMULA <- as.formula(paste(OUTCOME,PREDICTOR.F, sep = " ~ "))

# Summary samples in groups
dat.clean %>% group_by(across(all_of(PREDICTOR))) %>% get_summary_stats(!!sym(OUTCOME), type = "full") %>% select("day","feed","n","min","max","mean","sd","se")
## # A tibble: 6 × 8
##   day   feed      n   min   max  mean    sd    se
##   <chr> <chr> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
## 1 d08   HF       12 0.526 0.633 0.574 0.036 0.01 
## 2 d08   LF       11 0.457 0.596 0.531 0.044 0.013
## 3 d16   HF        6 0.454 0.494 0.477 0.017 0.007
## 4 d16   LF        6 0.36  0.479 0.433 0.044 0.018
## 5 d21   HF        6 0.476 0.58  0.526 0.034 0.014
## 6 d21   LF        6 0.431 0.54  0.495 0.042 0.017
dat.clean %>% group_by(across(all_of(PREDICTOR))) %>% get_summary_stats(!!sym("l_pct"), type = "full") %>% select("day","feed","n","min","max","mean","sd","se")
## # A tibble: 6 × 8
##   day   feed      n   min   max  mean    sd    se
##   <chr> <chr> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
## 1 d08   HF       12 0.367 0.474 0.426 0.036 0.01 
## 2 d08   LF       11 0.404 0.543 0.469 0.044 0.013
## 3 d16   HF        6 0.506 0.546 0.523 0.017 0.007
## 4 d16   LF        6 0.521 0.64  0.567 0.044 0.018
## 5 d21   HF        6 0.42  0.524 0.474 0.034 0.014
## 6 d21   LF        6 0.46  0.569 0.505 0.042 0.017
# Test for outliers
dat.clean %>% 
  group_by(across(all_of(PREDICTOR))) %>% 
  identify_outliers(!!sym(OUTCOME))
## # A tibble: 2 × 24
##   feed  day      id sample_name rat_name sample_org rat_org type  material    df
##   <chr> <chr> <int> <chr>       <chr>    <chr>      <chr>   <chr> <chr>    <int>
## 1 HF    d21     288 R46Ud21     R46      R46U21     R46     samp… Urine      400
## 2 HF    d21     289 R47Ud21     R47      R47U21     R47     samp… Urine      400
## # ℹ 14 more variables: batch_no <int>, notes <chr>, org_conc <dbl>,
## #   sample_conc_ug <dbl>, pfos_t_amt <dbl>, pfos_b_amt <dbl>, pfos_l_amt <dbl>,
## #   bl_ratio <dbl>, dayfeed <chr>, matfeed <chr>, br_pct <dbl>, l_pct <dbl>,
## #   is.outlier <lgl>, is.extreme <lgl>
# Check normality
# Build the linear model
model  <- lm(FORMULA, data = dat.clean)
# Create a QQ plot of residuals
ggqqplot(residuals(model))

# Compute Shapiro-Wilk test of normality
shapiro_test(residuals(model))
## # A tibble: 1 × 3
##   variable         statistic p.value
##   <chr>                <dbl>   <dbl>
## 1 residuals(model)     0.975   0.416
# Check the homogeneity of variances with Levene's test
dat.clean %>% levene_test(FORMULA)
## # A tibble: 1 × 4
##     df1   df2 statistic     p
##   <int> <int>     <dbl> <dbl>
## 1     5    41     0.674 0.646
# Save result
EQUAL.VAR <- dat.clean %>% levene_test(FORMULA) %>% pull(p) > 0.05

This shows that data has one none-critical outlier, is normally distribution and has equal variance. Therefore we can test the data with a one-way ANOVA test with Tukey’s honest significance test.

7.3.3.2 ANOVA One-Way test

Perform test

If we had equality of variance we can now run a one-way ANOVA tests anova_test() (if we have equal variance) or a welch_anova_test() (if variance vary).

if(EQUAL.VAR) {
  res.aov <- dat.clean %>% anova_test(FORMULA)
  res.aov
} else {
  res.aov <- dat.clean %>% welch_anova_test(FORMULA)
  res.aov
}
## ANOVA Table (type II tests)
## 
##     Effect DFn DFd      F        p p<.05   ges
## 1      day   2  41 26.099 4.89e-08     * 0.560
## 2     feed   1  41 12.888 8.76e-04     * 0.239
## 3 day:feed   2  41  0.100 9.05e-01       0.005

Perform posthoc test

if(EQUAL.VAR) {
  pwc <- dat.clean %>% tukey_hsd(FORMULA)
  pwc
} else {
  pwc <- dat.clean %>% games_howell_test(FORMULA)
  pwc
}
## # A tibble: 19 × 9
##    term     group1 group2 null.value estimate conf.low conf.high        p.adj
##  * <chr>    <chr>  <chr>       <dbl>    <dbl>    <dbl>     <dbl>        <dbl>
##  1 day      d08    d16             0 -0.0984  -0.131    -0.0654  0.0000000214
##  2 day      d08    d21             0 -0.0431  -0.0761   -0.0102  0.00771     
##  3 day      d16    d21             0  0.0552   0.0175    0.0930  0.00273     
##  4 feed     HF     LF              0 -0.0399  -0.0623   -0.0174  0.000878    
##  5 day:feed d08:HF d16:HF          0 -0.0968  -0.154    -0.0399  0.000118    
##  6 day:feed d08:HF d21:HF          0 -0.0476  -0.105     0.00924 0.147       
##  7 day:feed d08:HF d08:LF          0 -0.0423  -0.0898    0.00514 0.105       
##  8 day:feed d08:HF d16:LF          0 -0.140   -0.197    -0.0835  0.0000000711
##  9 day:feed d08:HF d21:LF          0 -0.0791  -0.136    -0.0222  0.00208     
## 10 day:feed d16:HF d21:HF          0  0.0492  -0.0165    0.115   0.243       
## 11 day:feed d16:HF d08:LF          0  0.0545  -0.00326   0.112   0.0742      
## 12 day:feed d16:HF d16:LF          0 -0.0436  -0.109     0.0221  0.369       
## 13 day:feed d16:HF d21:LF          0  0.0177  -0.0480    0.0834  0.965       
## 14 day:feed d21:HF d08:LF          0  0.00530 -0.0524    0.0630  1           
## 15 day:feed d21:HF d16:LF          0 -0.0928  -0.158    -0.0271  0.00172     
## 16 day:feed d21:HF d21:LF          0 -0.0315  -0.0971    0.0342  0.708       
## 17 day:feed d08:LF d16:LF          0 -0.0981  -0.156    -0.0403  0.000122    
## 18 day:feed d08:LF d21:LF          0 -0.0368  -0.0945    0.0210  0.415       
## 19 day:feed d16:LF d21:LF          0  0.0613  -0.00437   0.127   0.0795      
## # ℹ 1 more variable: p.adj.signif <chr>

Significant impact is observed on both day and feed. We will plot this as a nested analysis with t-test and ANOVA with Tukey’s HSD on the inner variable and the outer variable.

7.3.3.3 Create figure

## Pairwise comparison for inner variable: day
stat.in <- dat.clean %>%
  group_by(day) %>%
  t_test(as.formula(paste0(OUTCOME," ~ feed")), 
         paired = FALSE, var.equal = EQUAL.VAR, 
         detailed = TRUE, alternative = "two.sided") %>%
  add_significance() %>%
  p_format("p", accuracy = 0.001, trailing.zero = TRUE, new.col = TRUE)
stat.in
## # A tibble: 3 × 18
##   day   estimate estimate1 estimate2 .y.    group1 group2    n1    n2 statistic
## * <chr>    <dbl>     <dbl>     <dbl> <chr>  <chr>  <chr>  <int> <int>     <dbl>
## 1 d08     0.0423     0.574     0.531 br_pct HF     LF        12    11      2.53
## 2 d16     0.0436     0.477     0.433 br_pct HF     LF         6     6      2.28
## 3 d21     0.0315     0.526     0.495 br_pct HF     LF         6     6      1.42
## # ℹ 8 more variables: p <dbl>, df <dbl>, conf.low <dbl>, conf.high <dbl>,
## #   method <chr>, alternative <chr>, p.signif <chr>, p.format <chr>
## Pairwise comparison for outer variable
stat.out <- dat.clean %>%
  anova_test(as.formula(paste0(OUTCOME," ~ day"))) %>%
  add_significance() %>%
  p_format("p", accuracy = 0.001, trailing.zero = TRUE, new.col = TRUE)
stat.out
## ANOVA Table (type II tests)
## 
##   Effect DFn DFd      F        p p<.05   ges p.signif p.format
## 1    day   2  44 21.641 2.85e-07     * 0.496     ****   <0.001
pwc2 <- dat.clean %>%
  tukey_hsd(as.formula(paste0(OUTCOME," ~ day"))) %>%
  add_significance() %>%
  p_format("p.adj", accuracy = 0.001, trailing.zero = TRUE, new.col = TRUE)
pwc2
## # A tibble: 3 × 10
##   term  group1 group2 null.value estimate conf.low conf.high       p.adj
## * <chr> <chr>  <chr>       <dbl>    <dbl>    <dbl>     <dbl>       <dbl>
## 1 day   d08    d16             0  -0.0984  -0.135   -0.0619  0.000000158
## 2 day   d08    d21             0  -0.0431  -0.0796  -0.00667 0.017      
## 3 day   d16    d21             0   0.0552   0.0135   0.0970  0.00694    
## # ℹ 2 more variables: p.adj.signif <chr>, p.adj.format <chr>
## Calculate positions statistics on plot
stat.in <- stat.in %>% add_xy_position(x = "day", dodge = 0.8)
pwc2 <- pwc2 %>% add_xy_position(x = "day")
pwc2$y.position <- max(stat.in$y.position)*1.1

# Create plot
p <- ggboxplot(dat.clean, x = "day", y = OUTCOME,
               fill = "feed",
               color = "feed",
               add = "jitter",
               add.params = list(size = 1)) +
  theme_pubr(legend = "top") +
  scale_color_manual(values = c("LF" = "black","HF" = "black")) +
  scale_fill_manual(values = params$COLFEED, name = "Feed") +
  scale_x_discrete(name = "Day", labels = c("d08" = "Day 8","d16" = "Day 16","d21" = "Day 21")) +
  theme(axis.title.x = element_blank()) +
  guides(color = "none")

if (OUTCOME == "bl_ratio") {
p.stat <- p + stat_pvalue_manual(stat.in, tip.length = 0, hide.ns = TRUE, color = "red") +
  stat_pvalue_manual(pwc2, tip.length = 0, hide.ns = TRUE, y.position = c(1.95, 2.05, 1.85)) +
  scale_y_continuous(name = "Urine B/L ratio")
} else if (OUTCOME == "br_pct") {
p.stat <- p + stat_pvalue_manual(stat.in, tip.length = 0, hide.ns = TRUE, color = "red") +
  stat_pvalue_manual(pwc2, tip.length = 0, hide.ns = TRUE, y.position = c(0.67, 0.69, 0.65)) +
  scale_y_continuous(name = "Urine br-PFOS %", labels = function(x) paste0(x*100, "%"))
}
p.stat

suppressMessages(ggsave(filename = paste0("plots/animal_data/pfos/isomer_",OUTCOME,"_urine.pdf"), plot = p.stat, device = "pdf", dpi = 300, units = "mm", height = 100, width = 100))
suppressMessages(ggsave(filename = paste0("plots/animal_data/pfos/isomer_",OUTCOME,"_urine.png"), plot = p.stat, device = "png", dpi = 300, units = "mm", height = 100, width = 100))

# BETWEEN DIETS
## Pairwise comparison for inner variable: day
stat.in2 <- dat.clean %>%
  group_by(feed) %>%
  anova_test(as.formula(paste0(OUTCOME," ~ day"))) %>%
  add_significance() %>%
  p_format("p", accuracy = 0.001, trailing.zero = TRUE, new.col = TRUE)
stat.in2
## # A tibble: 2 × 10
##   feed  Effect   DFn   DFd     F         p `p<.05`   ges p.signif p.format
## * <chr> <chr>  <dbl> <dbl> <dbl>     <dbl> <chr>   <dbl> <chr>    <chr>   
## 1 HF    day        2    21 18.5  0.0000229 *       0.639 ****     <0.001  
## 2 LF    day        2    20  9.94 0.001     *       0.499 ***      0.001
pwc.in2 <- dat.clean %>%
  group_by(feed) %>%
  tukey_hsd(as.formula(paste0(OUTCOME," ~ day"))) %>%
  add_significance() %>%
  p_format("p.adj", accuracy = 0.001, trailing.zero = TRUE, new.col = TRUE)
pwc.in2
## # A tibble: 6 × 11
##   feed  term  group1 group2 null.value estimate conf.low conf.high     p.adj
## * <chr> <chr> <chr>  <chr>       <dbl>    <dbl>    <dbl>     <dbl>     <dbl>
## 1 HF    day   d08    d16             0  -0.0968 -0.137    -0.0562  0.0000168
## 2 HF    day   d08    d21             0  -0.0476 -0.0883   -0.00701 0.0198   
## 3 HF    day   d16    d21             0   0.0492  0.00226   0.0961  0.0389   
## 4 LF    day   d08    d16             0  -0.0981 -0.154    -0.0424  0.000677 
## 5 LF    day   d08    d21             0  -0.0368 -0.0924    0.0189  0.241    
## 6 LF    day   d16    d21             0   0.0613 -0.00200   0.125   0.0588   
## # ℹ 2 more variables: p.adj.signif <chr>, p.adj.format <chr>
## Pairwise comparison for outer variable
stat.out2 <- dat.clean %>%
  t_test(as.formula(paste0(OUTCOME," ~ feed")),
         paired = FALSE, var.equal = EQUAL.VAR,
         detailed = TRUE, alternative = "two.sided") %>%
  add_significance() %>%
  p_format("p", accuracy = 0.001, trailing.zero = TRUE, new.col = TRUE)
stat.out2
## # A tibble: 1 × 17
##   estimate estimate1 estimate2 .y.    group1 group2    n1    n2 statistic     p
## *    <dbl>     <dbl>     <dbl> <chr>  <chr>  <chr>  <int> <int>     <dbl> <dbl>
## 1   0.0414     0.538     0.496 br_pct HF     LF        24    23      2.59 0.013
## # ℹ 7 more variables: df <dbl>, conf.low <dbl>, conf.high <dbl>, method <chr>,
## #   alternative <chr>, p.signif <chr>, p.format <chr>
## Calculate positions statistics on plot
pwc.in2 <- pwc.in2 %>% add_xy_position(x = "feed", dodge = 0.8)
stat.out2 <- stat.out2 %>% add_xy_position(x = "feed")
stat.out2$y.position <- max(pwc.in2$y.position)*1.1

# Create plot
p <- ggboxplot(dat.clean, x = "feed", y = OUTCOME,
               fill = "feed",
               color = "day",
               shape = "day",
               add = "jitter",
               add.params = list(size = 1, position_jitterdodge(dodge.width = 0.8))) +
  theme_pubr() + 
  scale_color_manual(values = params$COL_4BLACK) +#, name = "Day", labels = c("d08" = "8", "d12" = "12", "d16" = "16","d21" = "21")) +
  scale_fill_manual(values = params$COLFEED, name = "Feed") +
  scale_shape_manual(values = c("d08" = 19, "d12" = 1, "d16" = 17, "d21" = 2), name = "Day", labels = c("d08" = "8", "d12" = "12", "d16" = "16","d21" = "21")) +
  scale_x_discrete(name = "Feed") +
  theme(axis.title.x = element_blank()) +
  guides(color = "none", fill = "none", shape = "none")

if (OUTCOME == "bl_ratio") {
p.stat2 <- p + stat_pvalue_manual(pwc.in2, tip.length = 0, hide.ns = TRUE, color = "red") +
  stat_pvalue_manual(stat.out2, tip.length = 0, hide.ns = TRUE) +
  scale_y_continuous(name = "Urine B/L ratio")
} else if (OUTCOME == "br_pct") {
p.stat2 <- p + stat_pvalue_manual(pwc.in2, tip.length = 0, hide.ns = TRUE, color = "red") +
  stat_pvalue_manual(stat.out2, tip.length = 0, hide.ns = TRUE, y.position = 0.73) +
  scale_y_continuous(name = "Urine br-PFOS %", labels = function(x) paste0(x*100, "%"))
}
p.stat2

suppressMessages(ggsave(filename = paste0("plots/animal_data/pfos/isomer_",OUTCOME,"_urine_feed.pdf"), plot = p.stat2, device = "pdf", dpi = 300, units = "mm", height = 80, width = 100))
suppressMessages(ggsave(filename = paste0("plots/animal_data/pfos/isomer_",OUTCOME,"_urine_feed.png"), plot = p.stat2, device = "png", dpi = 300, units = "mm", height = 80, width = 100))

p.urine1 <- p.stat
p.urine2 <- p.stat2
save(p.urine1, p.urine2, file = "plots/animal_data/pfos/urine_isomer.Rdata")

# clear the environment and release memory
rm(list = ls(all.names = TRUE)[ls(all.names = TRUE) != "params"])
invisible(gc())

7.3.3.4 Conclusion

From BL-ratio of urine, we can see significant differences between sampling days and a trend towards higher BL-ratio in HF animals on Day 8 and 16, while only being significant on Day 8. Interestingly, Day 16 is significantly lower than both Day 8 and 21, which correlates with a significantly higher total PFOS concentration on Day 16 in urine. Overall the BL-ratio is very high compared to other materials, including feces and caecal content, indicating that br-PFOS is more readily expelled through urine in comparison.

7.4 ALL DATA

7.4.1 Material test

7.4.1.1 Prepare test of “material” day 8

Here we aim to test differences in bl-ratio between systemic and excreted samples on Day 8 and 21. We exclude Day 12 and 16, as not all sample types were recorded on these days. Included in the samples presented here are spiked negative controls and solvent controls which all have had the same batch of PFOS added directly to the same before analysis. These controls reflect the batch proportion of l-PFOS to br-PFOS.

load("R_objects/pfos_isomer_data.Rdata")

# Subset
dat.clean <- subset(dat, !type %in% c("suspension"))

# Set names of variables
PREDICTOR <- "matfeed"
OUTCOME <- "br_pct" #"bl_ratio"
SUBJECT <- "rat_name"

# Create formula
PREDICTOR.F <- ifelse(length(PREDICTOR) > 1, paste(PREDICTOR, collapse = "*"), PREDICTOR)
FORMULA <- as.formula(paste(OUTCOME,PREDICTOR.F, sep = " ~ "))

# Summary samples in groups
dat.clean %>% group_by(across(all_of(PREDICTOR))) %>% get_summary_stats(!!sym(OUTCOME), type = "mean_sd")
## # A tibble: 13 × 5
##    matfeed    variable     n  mean    sd
##    <chr>      <fct>    <dbl> <dbl> <dbl>
##  1 Brain_HF   br_pct      10 0.179 0.047
##  2 Brain_LF   br_pct      11 0.165 0.044
##  3 Cecum_HF   br_pct      12 0.198 0.013
##  4 Cecum_LF   br_pct      12 0.181 0.026
##  5 Feces_HF   br_pct      30 0.21  0.025
##  6 Feces_LF   br_pct      30 0.202 0.02 
##  7 Liver_HF   br_pct      12 0.298 0.036
##  8 Liver_LF   br_pct      12 0.246 0.079
##  9 Serum_HF   br_pct      24 0.362 0.035
## 10 Serum_LF   br_pct      24 0.357 0.033
## 11 Urine_HF   br_pct      24 0.538 0.051
## 12 Urine_LF   br_pct      23 0.496 0.058
## 13 posctrl_HF br_pct       9 0.102 0.011
# Test for outliers
dat.clean %>% 
  group_by(across(all_of(PREDICTOR))) %>% 
  identify_outliers(!!sym(OUTCOME))
## # A tibble: 8 × 24
##   matfeed     id sample_name rat_name sample_org rat_org type   feed  material
##   <chr>    <int> <chr>       <chr>    <chr>      <chr>   <chr>  <chr> <chr>   
## 1 Brain_LF   114 R20Bd21     R20      R20B21     R20     sample LF    Brain   
## 2 Cecum_LF   310 R20Cd21     R20      R20C21     R20     sample LF    Cecum   
## 3 Feces_HF   399 R45Fd08     R45      R45F8      R45     sample HF    Feces   
## 4 Feces_HF   470 R44Fd21     R44      R42F21     R42     sample HF    Feces   
## 5 Feces_HF   474 R48Fd21     R48      R48F21     R48     sample HF    Feces   
## 6 Feces_LF   433 R19Fd16     R19      R19F16     R19     sample LF    Feces   
## 7 Feces_LF   458 R20Fd21     R20      R20F21     R20     sample LF    Feces   
## 8 Feces_LF   460 R22Fd21     R22      R22F21     R22     sample LF    Feces   
## # ℹ 15 more variables: day <chr>, df <int>, batch_no <int>, notes <chr>,
## #   org_conc <dbl>, sample_conc_ug <dbl>, pfos_t_amt <dbl>, pfos_b_amt <dbl>,
## #   pfos_l_amt <dbl>, bl_ratio <dbl>, dayfeed <chr>, br_pct <dbl>, l_pct <dbl>,
## #   is.outlier <lgl>, is.extreme <lgl>
# Check normality
# Build the linear model
model  <- lm(FORMULA, data = dat.clean)
# Create a QQ plot of residuals
ggqqplot(residuals(model))

# Compute Shapiro-Wilk test of normality
shapiro_test(residuals(model))
## # A tibble: 1 × 3
##   variable         statistic  p.value
##   <chr>                <dbl>    <dbl>
## 1 residuals(model)     0.974 0.000251
# Check the homogeneity of variances with Levene's test
dat.clean %>% levene_test(FORMULA)
## Warning in leveneTest.default(y = y, group = group, ...): group coerced to
## factor.
## # A tibble: 1 × 4
##     df1   df2 statistic             p
##   <int> <int>     <dbl>         <dbl>
## 1    12   220      6.26 0.00000000172
# Save result
EQUAL.VAR <- dat.clean %>% levene_test(FORMULA) %>% pull(p) > 0.05
## Warning in leveneTest.default(y = y, group = group, ...): group coerced to
## factor.

This shows that data has two critical outliers, is not normally distribution and does not have equal variance. Therefore we can test the data with a Kruskal-Wallis with Dunn’s test adjusted p-values.

7.4.1.2 Kruskal-Wallis test

Perform test

res.aov <- dat.clean %>% kruskal_test(FORMULA)
res.aov
## # A tibble: 1 × 6
##   .y.        n statistic    df        p method        
## * <chr>  <int>     <dbl> <int>    <dbl> <chr>         
## 1 br_pct   233      201.    12 1.84e-36 Kruskal-Wallis

Effect size

The eta squared, based on the H-statistic, can be used as the measure of the Kruskal-Wallis test effect size. The interpretation values commonly in published literature are: 0.01- < 0.06 (small effect), 0.06 - < 0.14 (moderate effect) and >= 0.14 (large effect).

dat.clean %>% kruskal_effsize(FORMULA)
## # A tibble: 1 × 5
##   .y.        n effsize method  magnitude
## * <chr>  <int>   <dbl> <chr>   <ord>    
## 1 br_pct   233   0.860 eta2[H] large

Post-hoc test if interaction is significant

A significant Kruskal-Wallis test is generally followed up by Dunn’s test to identify which groups are different. It’s also possible to use the Wilcoxon’s test to calculate pairwise comparisons between group levels with corrections for multiple testing.

# pairwise comparisons
pwc <- dat.clean %>% 
  dunn_test(FORMULA, p.adjust.method = "fdr") 
pwc
## # A tibble: 78 × 9
##    .y.    group1   group2        n1    n2 statistic       p   p.adj p.adj.signif
##  * <chr>  <chr>    <chr>      <int> <int>     <dbl>   <dbl>   <dbl> <chr>       
##  1 br_pct Brain_HF Brain_LF      10    11   -0.560  5.76e-1 6.15e-1 ns          
##  2 br_pct Brain_HF Cecum_HF      10    12    0.607  5.44e-1 6.01e-1 ns          
##  3 br_pct Brain_HF Cecum_LF      10    12    0.0323 9.74e-1 9.74e-1 ns          
##  4 br_pct Brain_HF Feces_HF      10    30    1.36   1.73e-1 2.29e-1 ns          
##  5 br_pct Brain_HF Feces_LF      10    30    0.974  3.30e-1 3.97e-1 ns          
##  6 br_pct Brain_HF Liver_HF      10    12    2.90   3.70e-3 7.60e-3 **          
##  7 br_pct Brain_HF Liver_LF      10    12    1.53   1.25e-1 1.81e-1 ns          
##  8 br_pct Brain_HF posctrl_HF    10     9   -1.43   1.53e-1 2.09e-1 ns          
##  9 br_pct Brain_HF Serum_HF      10    24    4.42   1.00e-5 3.12e-5 ****        
## 10 br_pct Brain_HF Serum_LF      10    24    4.34   1.43e-5 4.28e-5 ****        
## # ℹ 68 more rows

Several groups show significant difference based of day and feed. We will plot this as a nested analysis with feed as the inner variable using a t-test and day as the outer variable using kruskal-wallis with posthoc Dunn’s test. Data from Day 8 and 21 are both included in analysis and plotted data in this analysis but with visually separate presentation.

7.4.1.3 Create figure

# Order the data
df_order <- c("posctrl","Brain","Liver","Serum","Cecum","Feces","Urine") #old: c("Serum","Liver","Brain","Feces","Cecum","Urine","posctrl")
dat.clean$material <- factor(as.character(dat.clean$material), levels = df_order)
dat.clean <- dat.clean[order(dat.clean$material),]

## Pairwise comparison for inner variable
stat.in <- dat.clean %>% subset(!material == "posctrl") %>%
  group_by(material) %>%
  wilcox_test(as.formula(paste0(OUTCOME," ~ feed")),
              paired = FALSE) %>%
  add_significance() %>%
  p_format("p", accuracy = 0.001, trailing.zero = TRUE, new.col = TRUE)
stat.in
## # A tibble: 6 × 10
##   material .y.    group1 group2    n1    n2 statistic      p p.signif p.format
## * <fct>    <chr>  <chr>  <chr>  <int> <int>     <dbl>  <dbl> <chr>    <chr>   
## 1 Brain    br_pct HF     LF        10    11        64 0.557  ns       0.557   
## 2 Liver    br_pct HF     LF        12    12       104 0.0684 ns       0.068   
## 3 Serum    br_pct HF     LF        24    24       309 0.675  ns       0.675   
## 4 Cecum    br_pct HF     LF        12    12       101 0.101  ns       0.101   
## 5 Feces    br_pct HF     LF        30    30       550 0.142  ns       0.142   
## 6 Urine    br_pct HF     LF        24    23       387 0.0177 *        0.018
## Pairwise comparison for outer variable
stat.out <- dat.clean %>%
  kruskal_test(as.formula(paste0(OUTCOME," ~ material"))) %>%
  add_significance() %>%
  p_format("p", accuracy = 0.001, trailing.zero = TRUE, new.col = TRUE)
stat.out
## # A tibble: 1 × 8
##   .y.        n statistic    df        p method         p.signif p.format
## * <chr>  <int>     <dbl> <int>    <dbl> <chr>          <chr>    <chr>   
## 1 br_pct   233      198.     6 5.43e-40 Kruskal-Wallis ****     <0.001
pwc2 <- dat.clean %>%
  dunn_test(as.formula(paste0(OUTCOME," ~ material"))) %>%
  add_significance() %>%
  p_format("p.adj", accuracy = 0.001, trailing.zero = TRUE, new.col = TRUE)
pwc2
## # A tibble: 21 × 10
##    .y.    group1  group2    n1    n2 statistic        p    p.adj p.adj.signif
##  * <chr>  <chr>   <chr>  <int> <int>     <dbl>    <dbl>    <dbl> <chr>       
##  1 br_pct posctrl Brain      9    21     1.33  1.84e- 1 5.53e- 1 ns          
##  2 br_pct posctrl Liver      9    24     4.11  3.94e- 5 4.73e- 4 ***         
##  3 br_pct posctrl Serum      9    48     6.35  2.22e-10 3.55e- 9 ****        
##  4 br_pct posctrl Cecum      9    24     2.03  4.22e- 2 1.73e- 1 ns          
##  5 br_pct posctrl Feces      9    60     3.03  2.44e- 3 2.20e- 2 *           
##  6 br_pct posctrl Urine      9    47     8.33  8.28e-17 1.49e-15 ****        
##  7 br_pct Brain   Liver     21    24     3.61  3.09e- 4 3.40e- 3 **          
##  8 br_pct Brain   Serum     21    48     6.79  1.13e-11 1.93e-10 ****        
##  9 br_pct Brain   Cecum     21    24     0.887 3.75e- 1 5.53e- 1 ns          
## 10 br_pct Brain   Feces     21    60     2.19  2.88e- 2 1.73e- 1 ns          
## # ℹ 11 more rows
## # ℹ 1 more variable: p.adj.format <chr>
## Calculate positions statistics on plot
stat.in <- stat.in %>% add_xy_position(x = "material", dodge = 0.8)
pwc2 <- pwc2 %>% add_xy_position(x = "material")
pwc2$y.position <- max(stat.in$y.position)*1.1

# Mean and SD
dat.sum <- dat.clean %>% group_by(across(all_of(c("material","feed")))) %>% get_summary_stats(!!sym(OUTCOME), type = "mean_sd")
dat.sum$material <- factor(as.character(dat.sum$material), levels = df_order)
dat.sum <- dat.sum[order(dat.sum$material),]

# Create plot
p <- ggplot() +
  geom_crossbar(data=dat.sum, 
                aes(x = material,y = mean, ymin = mean, ymax = mean, color = feed),
                linewidth=0.1, width = .7, 
                position = position_dodge(width = 0.8)) +
  geom_errorbar(data=dat.sum, 
                aes(x = material,y = mean, ymin = mean-sd, ymax = mean+sd, color = feed),
                linewidth=0.1, width = .3, 
                position = position_dodge(width = 0.8)) +
  geom_point(data=dat.clean, 
             mapping = aes(x=material, y=.data[[OUTCOME]], color=feed), size = 1,
             position = position_jitterdodge(dodge.width = 0.8, jitter.width = 0.3)) +
  scale_color_manual(values = params$COLFEED, name = "Feed") +
  scale_x_discrete(labels = c("posctrl" = "Reference", "Feces" = "Faeces", "Cecum" = "Caecum"))

if (OUTCOME == "bl_ratio") {
p.stat <- p + stat_pvalue_manual(stat.in, label = "p.signif", tip.length = 0, hide.ns = TRUE, color = "red", y.position = c(0.8, 1.7)) +
  stat_pvalue_manual(pwc2, label = "p.adj.signif", tip.length = 0, hide.ns = TRUE, y.position = c(1.1,1.2,1.3,2.2,2.5,1,2.1,2,2.4,1.9,2.3,1.8,2.15)) +
  scale_y_continuous(name = "B/L ratio",expand = expansion(mult = c(0.01, 0.1)), limits = c(0.05,2.5), breaks = seq(0.00,2.5,0.25)) +
  theme_pubr(legend = "left") +
  theme(axis.title.x = element_blank(), legend.position = c(0.99, .3),
    legend.justification = c("right", "top"),
    legend.box.just = "left",
    legend.margin = margin(4, 4, 4, 4)) +
  guides(color = guide_legend(override.aes = list(shape = 15, size = 4, linetype = c(0,0))))
} else if (OUTCOME == "br_pct") {
p.stat <- p + stat_pvalue_manual(stat.in, label = "p.signif", tip.length = 0, hide.ns = TRUE, color = "red", y.position = 0.65) +
  stat_pvalue_manual(pwc2, label = "p.adj.signif", tip.length = 0, hide.ns = TRUE, y.position = c(0.45,0.5,0.625,0.675, 0.475,0.525,0.7, 0.55,0.575,0.725, 0.6,0.65,0.75, 0.775, 0.8)) +
  scale_y_continuous(name = " br-PFOS %", labels = function(x) paste0(x*100, "%"),  limits = c(0,0.82), breaks = seq(0.00,0.82,0.2)) +
  theme_pubr(legend = "left") +
  theme(axis.title.x = element_blank(), legend.position = c(0.99, .3),
    legend.justification = c("right", "top"),
    legend.box.just = "left",
    legend.margin = margin(4, 4, 4, 4)) +
  guides(color = guide_legend(override.aes = list(shape = 15, size = 4, linetype = c(0,0))))

# Letter code for legend explanation (attempt same-as code)
letter <- data.frame(material = c("posctrl","Brain","Liver","Serum","Cecum","Feces","Urine"), sig = c("abe","abef","ef","d","abef","bcef","g"), y = c(0.17,0.32,0.47,0.5,0.3,0.35,0.74))

# Plot with letter explanation
p.letter <- p + stat_pvalue_manual(stat.in, label = "p.signif", tip.length = 0, hide.ns = TRUE, color = "red") +
  scale_y_continuous(name = " br-PFOS %", labels = function(x) paste0(x*100, "%"),  limits = c(0,0.75), breaks = seq(0.00,0.75,0.1)) +
  geom_text(data = letter, aes(x = material, y = y, label = sig, fontface = "bold.italic"), size = 4) +
  theme_pubr(legend = "top") +
  theme(axis.title.x = element_blank(),
        legend.position = c(0.5, 1),
        legend.justification = c("top"),
        legend.direction = "horizontal",
        legend.margin = margin(4, 4, 4, 4)) +
  guides(color = guide_legend(override.aes = list(shape = 15, size = 4, linetype = c(0,0)))) +
  scale_x_discrete(labels = c("posctrl" = "Reference\n(a)","Brain" = "Brain\n(b)","Liver" = "Liver\n(c)","Serum" = "Serum\n(d)", "Cecum" = "Caecum\n(e)", "Feces" = "Faeces\n(f)", "Urine" = "Urine\n(g)"))
}
## Warning: A numeric `legend.position` argument in `theme()` was deprecated in ggplot2
## 3.5.0.
## ℹ Please use the `legend.position.inside` argument of `theme()` instead.
## This warning is displayed once every 8 hours.
## Call `lifecycle::last_lifecycle_warnings()` to see where this warning was
## generated.
## Scale for x is already present.
## Adding another scale for x, which will replace the existing scale.
p.stat

p.letter

suppressMessages(ggsave(filename = paste0("plots/animal_data/pfos/isomer_",OUTCOME,"_material.pdf"), plot = p.stat, device = "pdf", dpi = 300, units = "mm", height = 100, width = 200))
suppressMessages(ggsave(filename = paste0("plots/animal_data/pfos/isomer_",OUTCOME,"_material.png"), plot = p.stat, device = "png", dpi = 300, units = "mm", height = 100, width = 200))
suppressMessages(ggsave(filename = paste0("plots/animal_data/pfos/isomer_",OUTCOME,"_material_letter.pdf"), plot = p.letter, device = "pdf", dpi = 300, units = "mm", height = 100, width = 200)) #250 before
suppressMessages(ggsave(filename = paste0("plots/animal_data/pfos/isomer_",OUTCOME,"_material_letter.png"), plot = p.letter, device = "png", dpi = 300, units = "mm", height = 100, width = 200))

p.mat_stat <- p.stat
p.mat_letter <- p.letter
save(p.mat_stat, p.mat_letter, file = "plots/animal_data/pfos/mat_isomer.Rdata")

# clear the environment and release memory
rm(list = ls(all.names = TRUE)[ls(all.names = TRUE) != "params"])
invisible(gc())

7.4.1.4 Conclusion

Large significant differences observed between samples types (material), where all but cecum and brain were significantly higher than control samples. Among the systemic samples, the BL-ratio was highest in serum > liver > brain which interestingly correlated directly to the same order in total PFOS concentrations. Highest BL-ratio is observed in urine, suggesting that br-PFOS is more readily expelled by urine than by feces. Differences in feed show higher BL-ratio in HF in liver and urine samples, while no differences are observed in remaining sample types.

7.4.2 Combine plots

# Load rdata files with scfa plots
pfiles <- list.files(path = "plots/animal_data/pfos", pattern = "*isomer.Rdata", full.names = TRUE)
lapply(pfiles, load,.GlobalEnv)
## [[1]]
## [1] "p.brain1" "p.brain2"
## 
## [[2]]
## [1] "p.caecum1" "p.caecum2"
## 
## [[3]]
## [1] "p.faeces1" "p.faeces2"
## 
## [[4]]
## [1] "p.liver1" "p.liver2"
## 
## [[5]]
## [1] "p.mat_stat"   "p.mat_letter"
## 
## [[6]]
## [1] "p.serum1" "p.serum2"
## 
## [[7]]
## [1] "p.urine1" "p.urine2"
# Plot isomer plots per feed
p.all <- ggarrange(p.brain2,p.liver2,p.serum2,p.caecum2,p.faeces2,p.urine2,
                   ncol = 3, nrow = 2, 
                   common.legend = TRUE,
                   legend = "top",
                   label.x = 0,
                   font.label = list(size = 24, face = "bold"),
                   labels = c("B","C","D","E","F","G"),
                   align = "hv")
p.all

# Save graphics
ggsave(filename = "plots/animal_data/pfos/isomer_all.png", p.all, device = "png", dpi = 300, height = 160, width = 250, units = "mm")
ggsave(filename = "plots/animal_data/pfos/isomer_all.pdf", p.all, device = "pdf", dpi = 300, height = 160, width = 250, units = "mm")

# clear the environment and release memory
rm(list = ls(all.names = TRUE)) #will clear all objects includes hidden objects.
invisible(gc()) #free up memory and report the memory usage.

8 SHORT-CHAIN FATTY ACIDS

Concentrations in mM were recorded from caecal water samples from caecal content collected from all animals (except R04) at dissection.

8.1 Prepare SCFA data

# Load data
load("R_objects/animal_data.Rdata")
params <- readRDS("R_objects/animal_params.RDS")

dat.clean <- dat %>% select(rat_name, feed, treatment, feedtreat, dissection,
  acetic, formic, propanoic, m2_propanoic, butanoic, m3_butanoic, pentanoic, hexanoic)# not included due to low sample count: m4_pentanoic, heptanoic, 

# Subset data with NA
dat.clean <- subset(dat.clean, !dat.clean$rat_name == "R04") # Mostly <LOD - suspects error in sample handling

# Create SCFA table
row.names(dat.clean) <- dat.clean$rat_name
dat.SCFA <- dat.clean %>% select(acetic, formic, propanoic, m2_propanoic, butanoic, m3_butanoic, pentanoic, hexanoic) 
dat.SCFA
##        acetic    formic propanoic m2_propanoic   butanoic m3_butanoic
## R01  7.341875 0.5368092 1.8222638   0.11458476  0.8987209 0.054977624
## R02  7.855208 0.0000000 2.2070656   0.10691220  1.3073081 0.059441549
## R03  5.146957 0.0000000 2.4561816   0.00000000  0.7532571 0.000000000
## R05  7.982690 0.0000000 2.0544536   0.00000000  1.1110579 0.196319793
## R06  8.776675 0.2176932 2.7126279   0.15528998  1.8636544 0.066751284
## R07  9.639725 0.0000000 2.6859899   0.14239121  1.7281461 0.061447497
## R08  9.078671 0.0000000 2.2318548   0.18339766  1.3368317 0.091758195
## R09  8.897958 0.0000000 2.5053508   0.18053536  1.8769926 0.089813034
## R10  8.474208 0.0000000 2.0948772   0.12049304  3.8161456 0.069922162
## R11  7.155427 0.0000000 2.2205310   0.17458153  1.2040138 0.095099852
## R12  7.616896 0.6456507 1.5972133   0.20695849  1.9141834 0.100049621
## R13  9.667622 0.5837262 1.5603675   0.07482866  1.5538837 0.000000000
## R14  9.115617 0.3122245 2.0621057   0.11677311  1.2724029 0.045396199
## R15  4.641313 0.0000000 1.7988561   0.11952145  1.3137417 0.000000000
## R16  6.693080 0.0000000 2.4530337   0.13023905  1.2864006 0.073027831
## R17  8.877980 0.0000000 2.2304256   0.13800313  1.3169297 0.000000000
## R18  8.862873 0.5053084 1.5964168   0.19264865  1.2247779 0.000000000
## R19  7.951609 0.8993531 1.8304303   0.13977563  2.1861283 0.061983538
## R20  4.896264 0.2832883 1.8612969   0.18828803  1.0282520 0.109074443
## R21 11.985676 0.9859684 2.9109754   0.18071705  3.0569397 0.064346567
## R22  5.833129 0.0000000 1.8684475   0.12256397  1.4143747 0.058381178
## R23  9.654142 0.3776905 2.4182053   0.22178789  1.8719020 0.081073913
## R24  7.269043 0.3158913 2.2975509   0.20146583  1.8965003 0.089134967
## R25  8.419374 0.0000000 2.9202104   0.06544546  5.2225577 0.054289225
## R26  9.665720 0.0000000 5.8420110   0.12858333  2.3644827 0.050600100
## R27 12.538374 0.0000000 4.2183282   0.00000000  3.6613416 0.022126931
## R28 10.856261 0.0000000 1.9168245   0.05987570  8.0273412 0.048581262
## R29 15.170864 0.0000000 4.5226069   0.05354928  5.6681538 0.043748015
## R30 13.344693 0.6733067 2.6888836   0.00000000  4.5156249 0.000000000
## R31 13.265482 0.2137461 5.1847575   0.00000000  3.0914315 0.041388678
## R32 13.353800 0.0000000 3.0404715   0.08925862  9.9168104 0.070912110
## R33 12.063924 0.0000000 2.5849463   0.11912504  8.4525493 0.077120881
## R34 22.272322 0.0000000 4.3954526   0.06873288 11.6715032 0.064804495
## R35 13.505658 0.0000000 3.8940381   0.00000000  3.9437064 0.035908872
## R36 10.876729 0.0000000 1.2645777   0.10764445 11.3094552 0.000000000
## R37 11.278974 0.0000000 1.7892070   0.00000000 10.3384594 0.025993469
## R38  9.753901 0.0000000 1.6376468   0.00000000  8.4755453 0.039117832
## R39 15.438630 0.0000000 2.3605013   0.00000000  7.9623058 0.000000000
## R40  4.035739 0.0000000 0.4303487   0.00000000  0.7893027 0.006379562
## R41 10.050717 0.0000000 3.1157795   0.00000000  8.9074275 0.086542332
## R42 11.070575 0.0000000 3.4034263   0.00000000  8.8724006 0.000000000
## R43 10.123028 0.0000000 2.3501190   0.13122850  7.7081915 0.110326575
## R44 12.980268 0.0000000 3.8181246   0.00000000  3.4227372 0.015590715
## R45 10.372582 0.0000000 1.8797750   0.15641959  7.9450705 0.111311094
## R46 11.491241 0.0000000 2.8512537   0.00000000  2.0516026 0.000000000
## R47 13.238087 0.0000000 2.3206747   0.09039197  9.4090312 0.000000000
## R48 11.613649 0.0000000 4.1687121   0.00000000  2.4192800 0.032778659
##      pentanoic   hexanoic
## R01 0.14404969 0.00000000
## R02 0.20677422 0.00000000
## R03 0.00000000 0.00000000
## R05 0.00000000 0.00000000
## R06 0.25712467 0.00000000
## R07 0.29442662 0.00000000
## R08 0.25882488 0.00000000
## R09 0.23754051 0.00000000
## R10 0.21271455 0.01006928
## R11 0.21340815 0.00000000
## R12 0.24892344 0.00000000
## R13 0.16003737 0.00000000
## R14 0.23624010 0.00000000
## R15 0.13625584 0.00000000
## R16 0.18270472 0.00000000
## R17 0.20608063 0.00000000
## R18 0.19895460 0.00000000
## R19 0.23389303 0.00000000
## R20 0.19827676 0.00000000
## R21 0.27670456 0.00000000
## R22 0.17901674 0.01240264
## R23 0.30114933 0.00000000
## R24 0.26399449 0.00000000
## R25 0.17542959 0.00000000
## R26 0.19197483 0.00000000
## R27 0.12854350 0.01802084
## R28 0.16177640 0.03430010
## R29 0.20207998 0.02867673
## R30 0.09025981 0.01102509
## R31 0.21254619 0.00000000
## R32 0.15988369 0.00000000
## R33 0.22606772 0.00000000
## R34 0.18259935 0.17746828
## R35 0.18351147 0.00000000
## R36 0.16723953 0.20377836
## R37 0.18224357 0.01777665
## R38 0.14979775 0.00000000
## R39 0.12749008 0.00000000
## R40 0.00000000 0.00000000
## R41 0.21484340 0.17024430
## R42 0.18989833 0.00000000
## R43 0.25556194 0.00000000
## R44 0.13723693 0.00000000
## R45 0.28516897 0.01934100
## R46 0.08847509 0.00000000
## R47 0.20498220 0.03455655
## R48 0.06619971 0.01806011
# Change all zeros to NA
dat.clean[dat.clean == 0] <- NA

# Summary samples in groups
tb <- dat.clean %>% group_by(across(all_of("feedtreat"))) %>% get_summary_stats(type = "mean_sd")
tb
## # A tibble: 31 × 5
##    feedtreat variable         n   mean    sd
##    <chr>     <fct>        <dbl>  <dbl> <dbl>
##  1 HF_CTRL   acetic          12 12.9   3.48 
##  2 HF_CTRL   formic           2  0.444 0.325
##  3 HF_CTRL   propanoic       12  3.54  1.36 
##  4 HF_CTRL   m2_propanoic     8  0.087 0.029
##  5 HF_CTRL   butanoic        12  6.49  3.26 
##  6 HF_CTRL   m3_butanoic     10  0.051 0.017
##  7 HF_CTRL   pentanoic       12  0.173 0.037
##  8 HF_CTRL   hexanoic         6  0.079 0.087
##  9 HF_PFOS   acetic          12 11.0   2.72 
## 10 HF_PFOS   propanoic       12  2.51  1.04 
## # ℹ 21 more rows
dat.clean %>% group_by(across(all_of("feedtreat"))) %>% get_summary_stats(type = "full")
## Warning: There were 2 warnings in `mutate()`.
## The first warning was:
## ℹ In argument: `ci = abs(stats::qt(alpha/2, .data$n - 1) * .data$se)`.
## Caused by warning:
## ! There was 1 warning in `mutate()`.
## ℹ In argument: `ci = abs(stats::qt(alpha/2, .data$n - 1) * .data$se)`.
## Caused by warning in `stats::qt()`:
## ! NaNs produced
## ℹ Run `dplyr::last_dplyr_warnings()` to see the 1 remaining warning.
## # A tibble: 31 × 14
##    feedtreat variable     n   min    max median     q1     q3   iqr   mad   mean
##    <chr>     <fct>    <dbl> <dbl>  <dbl>  <dbl>  <dbl>  <dbl> <dbl> <dbl>  <dbl>
##  1 HF_CTRL   acetic      12 8.42  22.3   12.9   10.9   13.4   2.52  2.12  12.9  
##  2 HF_CTRL   formic       2 0.214  0.673  0.444  0.329  0.558 0.23  0.341  0.444
##  3 HF_CTRL   propano…    12 1.26   5.84   3.47   2.66   4.43  1.76  1.34   3.54 
##  4 HF_CTRL   m2_prop…     8 0.054  0.129  0.079  0.064  0.111 0.046 0.033  0.087
##  5 HF_CTRL   butanoic    12 2.36  11.7    5.44   3.87   8.82  4.94  3.66   6.49 
##  6 HF_CTRL   m3_buta…    10 0.022  0.077  0.05   0.042  0.062 0.02  0.016  0.051
##  7 HF_CTRL   pentano…    12 0.09   0.226  0.179  0.161  0.195 0.033 0.027  0.173
##  8 HF_CTRL   hexanoic     6 0.011  0.204  0.031  0.021  0.142 0.121 0.025  0.079
##  9 HF_PFOS   acetic      12 4.04  15.4   11.2   10.1   12.0   1.85  1.61  11.0  
## 10 HF_PFOS   propano…    12 0.43   4.17   2.36   1.86   3.19  1.33  0.952  2.51 
## # ℹ 21 more rows
## # ℹ 3 more variables: sd <dbl>, se <dbl>, ci <dbl>
save(dat.clean, dat.SCFA, tb, file = "R_objects/SCFA_data.Rdata")

# clear the environment and release memory
rm(list = ls(all.names = TRUE)[ls(all.names = TRUE) != "params"])
invisible(gc())

8.2 Concentration plots

8.2.1 Prepare conc. data

# Load data
load("R_objects/SCFA_data.Rdata")
params <- readRDS("R_objects/animal_params.RDS")

# Prepare data for faceted concentrations plots
dat.conc <- dat.clean %>% pivot_longer(., cols = c(acetic, formic, propanoic, m2_propanoic, butanoic, m3_butanoic, pentanoic, hexanoic), names_to = "compound", values_to = "mM") %>% mutate("comp_name" = case_when(compound == "formic" ~ "Formate", compound == "acetic" ~ "Acetate", compound == "propanoic" ~ "Propionate", compound == "m2_propanoic" ~ "Isobutyrate", compound == "butanoic" ~ "Butyrate", compound == "m3_butanoic" ~ "Isovalerate",compound == "pentanoic" ~ "Valerate", compound == "m4_pentanoic" ~ "Isocaproate", compound == "hexanoic" ~ "Caproate", compound == "heptanoic" ~ "Enanthate"))

dat.conc <- subset(dat.conc, !is.na(mM))

# Set names of variables
PREDICTOR <- c("feedtreat","comp_name") #"matfeed"
OUTCOME <- "mM"
SUBJECT <- "rat_name"

# Create formula
PREDICTOR.F <- ifelse(length(PREDICTOR) > 1, paste(PREDICTOR, collapse = "*"), PREDICTOR)
FORMULA <- as.formula(paste(OUTCOME,PREDICTOR.F, sep = " ~ "))

# Summary samples in groups
dat.conc %>% group_by(across(all_of(PREDICTOR))) %>% get_summary_stats(!!sym(OUTCOME), type = "mean_sd")
## # A tibble: 31 × 6
##    feedtreat comp_name   variable     n   mean    sd
##    <chr>     <chr>       <fct>    <dbl>  <dbl> <dbl>
##  1 HF_CTRL   Acetate     mM          12 12.9   3.48 
##  2 HF_CTRL   Butyrate    mM          12  6.49  3.26 
##  3 HF_CTRL   Caproate    mM           6  0.079 0.087
##  4 HF_CTRL   Formate     mM           2  0.444 0.325
##  5 HF_CTRL   Isobutyrate mM           8  0.087 0.029
##  6 HF_CTRL   Isovalerate mM          10  0.051 0.017
##  7 HF_CTRL   Propionate  mM          12  3.54  1.36 
##  8 HF_CTRL   Valerate    mM          12  0.173 0.037
##  9 HF_PFOS   Acetate     mM          12 11.0   2.72 
## 10 HF_PFOS   Butyrate    mM          12  6.52  3.34 
## # ℹ 21 more rows
dat.conc %>% group_by(across(all_of(c("feedtreat","comp_name","dissection")))) %>% get_summary_stats(!!sym(OUTCOME), type = "mean_sd")
## # A tibble: 59 × 7
##    feedtreat dissection comp_name   variable     n   mean     sd
##    <chr>     <chr>      <chr>       <fct>    <dbl>  <dbl>  <dbl>
##  1 HF_CTRL   d08        Acetate     mM           6 11.7    2.49 
##  2 HF_CTRL   d21        Acetate     mM           6 14.2    4.07 
##  3 HF_CTRL   d08        Butyrate    mM           6  4.91   1.93 
##  4 HF_CTRL   d21        Butyrate    mM           6  8.06   3.71 
##  5 HF_CTRL   d08        Caproate    mM           4  0.023  0.01 
##  6 HF_CTRL   d21        Caproate    mM           2  0.191  0.019
##  7 HF_CTRL   d08        Formate     mM           1  0.673 NA    
##  8 HF_CTRL   d21        Formate     mM           1  0.214 NA    
##  9 HF_CTRL   d08        Isobutyrate mM           4  0.077  0.035
## 10 HF_CTRL   d21        Isobutyrate mM           4  0.096  0.022
## # ℹ 49 more rows
dat.conc %>% group_by(across(all_of(c("feedtreat","comp_name","dissection")))) %>% get_summary_stats(!!sym(OUTCOME), type = "full")
## Warning: There were 5 warnings in `mutate()`.
## The first warning was:
## ℹ In argument: `ci = abs(stats::qt(alpha/2, .data$n - 1) * .data$se)`.
## Caused by warning:
## ! There was 1 warning in `mutate()`.
## ℹ In argument: `ci = abs(stats::qt(alpha/2, .data$n - 1) * .data$se)`.
## Caused by warning in `stats::qt()`:
## ! NaNs produced
## ℹ Run `dplyr::last_dplyr_warnings()` to see the 4 remaining warnings.
## # A tibble: 59 × 16
##    feedtreat dissection comp_name   variable     n    min    max median     q1
##    <chr>     <chr>      <chr>       <fct>    <dbl>  <dbl>  <dbl>  <dbl>  <dbl>
##  1 HF_CTRL   d08        Acetate     mM           6  8.42  15.2   11.7    9.96 
##  2 HF_CTRL   d21        Acetate     mM           6 10.9   22.3   13.3   12.4  
##  3 HF_CTRL   d08        Butyrate    mM           6  2.36   8.03   4.87   3.88 
##  4 HF_CTRL   d21        Butyrate    mM           6  3.09  11.7    9.18   5.07 
##  5 HF_CTRL   d08        Caproate    mM           4  0.011  0.034  0.023  0.016
##  6 HF_CTRL   d21        Caproate    mM           2  0.177  0.204  0.191  0.184
##  7 HF_CTRL   d08        Formate     mM           1  0.673  0.673  0.673  0.673
##  8 HF_CTRL   d21        Formate     mM           1  0.214  0.214  0.214  0.214
##  9 HF_CTRL   d08        Isobutyrate mM           4  0.054  0.129  0.063  0.058
## 10 HF_CTRL   d21        Isobutyrate mM           4  0.069  0.119  0.098  0.084
## # ℹ 49 more rows
## # ℹ 7 more variables: q3 <dbl>, iqr <dbl>, mad <dbl>, mean <dbl>, sd <dbl>,
## #   se <dbl>, ci <dbl>
# Test for outliers
dat.conc %>% 
  group_by(across(all_of(PREDICTOR))) %>% 
  identify_outliers(!!sym(OUTCOME))
## # A tibble: 10 × 10
##    feedtreat comp_name   rat_name feed  treatment dissection compound         mM
##    <chr>     <chr>       <chr>    <chr> <chr>     <chr>      <chr>         <dbl>
##  1 HF_CTRL   Acetate     R34      HF    CTRL      d21        acetic      22.3   
##  2 HF_CTRL   Valerate    R30      HF    CTRL      d08        pentanoic    0.0903
##  3 HF_PFOS   Acetate     R39      HF    PFOS      d08        acetic      15.4   
##  4 HF_PFOS   Acetate     R40      HF    PFOS      d08        acetic       4.04  
##  5 HF_PFOS   Caproate    R41      HF    PFOS      d08        hexanoic     0.170 
##  6 LF_CTRL   Acetate     R03      LF    CTRL      d08        acetic       5.15  
##  7 LF_CTRL   Butyrate    R10      LF    CTRL      d21        butanoic     3.82  
##  8 LF_CTRL   Isovalerate R05      LF    CTRL      d08        m3_butanoic  0.196 
##  9 LF_CTRL   Valerate    R01      LF    CTRL      d08        pentanoic    0.144 
## 10 LF_PFOS   Butyrate    R21      LF    PFOS      d21        butanoic     3.06  
## # ℹ 2 more variables: is.outlier <lgl>, is.extreme <lgl>
# Check normality
# Build the linear model
model  <- lm(FORMULA, data = dat.conc)
# Create a QQ plot of residuals
ggqqplot(residuals(model))

# Compute Shapiro-Wilk test of normality
shapiro_test(residuals(model))
## # A tibble: 1 × 3
##   variable         statistic  p.value
##   <chr>                <dbl>    <dbl>
## 1 residuals(model)     0.740 1.23e-20
# Check the homogeneity of variances with Levene's test
dat.conc %>% levene_test(FORMULA)
## # A tibble: 1 × 4
##     df1   df2 statistic        p
##   <int> <int>     <dbl>    <dbl>
## 1    30   248      6.00 1.40e-16
# Save result
EQUAL.VAR <- dat.conc %>% levene_test(FORMULA) %>% pull(p) > 0.05

This shows that data has ten outliers of which four are critical, it is not normally distribution and does not have equal variance, which is expected from the compiled SCFA data columns. Therefore we can test the data with a Kruskal-Wallis with Dunn’s test adjusted p-values, which will here be calculated per compound across feed and treatment groups presented in a faceted plot.

8.2.2 Create nested figure

# Order the data
df_order <- c("Formate","Acetate","Propionate","Isobutyrate","Butyrate","Isovalerate","Valerate","Caproate")
dat.conc$comp_name <- factor(as.character(dat.conc$comp_name), levels = df_order)
dat.conc <- dat.conc[order(dat.conc$comp_name),]

# Exclusion of Formate from statistical analysis, as pairwise analysis cannot be done on missing groups of data
dat.conc2 <- dat.conc %>% subset(!(comp_name %in% c("Formate")))

# Set variables for inner and outer analysis, and variable for facet
INNER.VAR <- "treatment"
OUTER.VAR <- "feed"
FACETVAR <- "comp_name"

# Statistics for facet by compound
stat.in <- dat.conc2 %>%
  group_by(.data[[FACETVAR]],.data[[OUTER.VAR]]) %>%
  wilcox_test(as.formula(paste("mM ~",INNER.VAR, sep = " ")), paired = FALSE) %>%
  adjust_pvalue(method = "BH") %>%
  add_significance("p.adj") %>% 
  p_format("p.adj", accuracy = 0.0001, trailing.zero = TRUE, new.col = TRUE)
stat.in
## # A tibble: 14 × 12
##    feed  comp_name   .y.   group1 group2    n1    n2 statistic      p p.adj
##  * <chr> <fct>       <chr> <chr>  <chr>  <int> <int>     <dbl>  <dbl> <dbl>
##  1 HF    Acetate     mM    CTRL   PFOS      12    12        97 0.16   0.747
##  2 LF    Acetate     mM    CTRL   PFOS      11    12        64 0.928  1    
##  3 HF    Propionate  mM    CTRL   PFOS      12    12       106 0.0519 0.594
##  4 LF    Propionate  mM    CTRL   PFOS      11    12        84 0.288  0.868
##  5 HF    Isobutyrate mM    CTRL   PFOS       8     3         3 0.0848 0.594
##  6 LF    Isobutyrate mM    CTRL   PFOS       9    12        52 0.917  1    
##  7 HF    Butyrate    mM    CTRL   PFOS      12    12        74 0.932  1    
##  8 LF    Butyrate    mM    CTRL   PFOS      11    12        59 0.695  1    
##  9 HF    Isovalerate mM    CTRL   PFOS      10     8        46 0.633  1    
## 10 LF    Isovalerate mM    CTRL   PFOS      10     8        49 0.46   1    
## 11 HF    Valerate    mM    CTRL   PFOS      12    11        68 0.928  1    
## 12 LF    Valerate    mM    CTRL   PFOS       9    12        69 0.31   0.868
## 13 HF    Caproate    mM    CTRL   PFOS       6     5        17 0.792  1    
## 14 LF    Caproate    mM    CTRL   PFOS       1     1         0 1      1    
## # ℹ 2 more variables: p.adj.signif <chr>, p.adj.format <chr>
stat.out <- dat.conc2 %>%
  group_by(.data[[FACETVAR]]) %>%
  wilcox_test(as.formula(paste("mM ~",OUTER.VAR, sep = " ")), paired = FALSE) %>%
  adjust_pvalue(method = "BH") %>%
  add_significance("p.adj") %>%
  p_format("p.adj", accuracy = 0.0001, trailing.zero = TRUE, new.col = TRUE)
stat.out
## # A tibble: 7 × 11
##   comp_name   .y.   group1 group2    n1    n2 statistic            p       p.adj
## * <fct>       <chr> <chr>  <chr>  <int> <int>     <dbl>        <dbl>       <dbl>
## 1 Acetate     mM    HF     LF        24    23       506 0.000000076      2.66e-7
## 2 Propionate  mM    HF     LF        24    23       409 0.00411          5.41e-3
## 3 Isobutyrate mM    HF     LF        11    21        35 0.000866         2.02e-3
## 4 Butyrate    mM    HF     LF        24    23       520 0.0000000054     3.78e-8
## 5 Isovalerate mM    HF     LF        18    18        74 0.00464          5.41e-3
## 6 Valerate    mM    HF     LF        23    21       116 0.00269          4.71e-3
## 7 Caproate    mM    HF     LF        11     2        21 0.0513           5.13e-2
## # ℹ 2 more variables: p.adj.signif <chr>, p.adj.format <chr>
## Calculate positions statistics on plot
stat.in <- stat.in %>% add_xy_position(x = OUTER.VAR, dodge = 0.8)
stat.out <- stat.out %>% add_xy_position(x = OUTER.VAR)
stat.out$y.position <- c(24,6.2,0.24,13,0.21,0.32,0.21) #manual adjustment

# Create list of mean and sd for plotting
dat.sum <- dat.conc %>% group_by(across(all_of(c("comp_name","feedtreat")))) %>% get_summary_stats(!!sym("mM"), type = "mean_sd")
dat.sum <- dat.sum %>% mutate("feed" = case_when(grepl("HF_",feedtreat) ~ "HF",
                                                 grepl("LF_",feedtreat) ~ "LF"))
dat.lod <- data.frame("comp_name" = c("Formate","Acetate","Propionate","Isobutyrate","Butyrate","Isovalerate","Valerate","Isocaproate","Caproate","Enanthate"),
                      "LOD" = c(0.18,0.1,0.07,0.05,0.02,0.01,0.01,0.09,0.01,0.04))
dat.lod <- subset(dat.lod, dat.lod$comp_name %in% c("Formate","Acetate","Propionate","Isobutyrate","Butyrate","Isovalerate","Valerate","Caproate"))

# Order the data
dat.lod$comp_name <- factor(as.character(dat.lod$comp_name), levels = df_order)
dat.lod <- dat.lod[order(dat.lod$comp_name),]

# Plot
p <- ggplot() +
  geom_hline(data = dat.lod, aes(yintercept = LOD), linetype = "dashed", color = "#666666") +
  geom_crossbar(data=dat.sum, 
                 aes(x = feed, y = mean, ymin = mean, ymax = mean, color = feedtreat), 
                 linewidth=0.1, width = 0.7,
                 position = position_dodge(width = 0.8)) +
  geom_errorbar(data=dat.sum, 
                aes(x = feed, y = mean, ymin = mean-sd, ymax = mean+sd, color = feedtreat),
                linewidth=0.1, width = 0.3,
                position = position_dodge(width = 0.8)) +
  geom_jitter(data = dat.conc, aes(x = feed, y = mM, color = feedtreat, shape = dissection), 
              position = position_jitterdodge(jitter.width = 0.1, dodge.width = 0.8)) +
  scale_x_discrete(name = "Group", labels = c("HF","LF")) +
  scale_color_manual(values = params$COL1, name = "Group", labels = c("HF-CTRL","HF-PFOS","LF-CTRL","LF-PFOS")) +
  scale_shape_manual(name = "Day", label = c("d08" = "8","d21" = "21"), values = c("d08" = 19, "d12" = 1, "d16" = 17, "d21" = 2)) +
  theme_pubr(border = TRUE) +
  theme(axis.title.x = element_blank()) +
  facet_wrap(.~comp_name, ncol = 4, nrow = 2, scales = "free_y") +
  expand_limits(y = 0) +
  guides(color = guide_legend(override.aes = list(size = 4, shape = 15, linetype = 0)), 
         shape = guide_legend(override.aes = list(size = 3)))

# Add statistics
p.stat <- p + stat_pvalue_manual(stat.in, label = "p.adj.signif", tip.length = 0, hide.ns = TRUE, color = "red") +
  stat_pvalue_manual(stat.out, label = "p.adj.signif", tip.length = 0, hide.ns = TRUE) +
  scale_y_continuous(expand = expansion(mult = c(0,0.1)), name = "mM compound")
p.stat

# Save plot
suppressMessages(ggsave(filename = "plots/animal_data/scfa/scfa_concentrations_nested.pdf", plot = p.stat, device = "pdf", dpi = 300, units = "mm", height = 150, width = 200))
suppressMessages(ggsave(filename = "plots/animal_data/scfa/scfa_concentrations_nested.png", plot = p.stat, device = "png", dpi = 300, units = "mm", height = 150, width = 200))

# clear the environment and release memory
rm(list = ls(all.names = TRUE)[ls(all.names = TRUE) != "params"])
invisible(gc())

8.3 Principal component analysis (PCA)

8.3.1 DAtest (treatment)

load("R_objects/SCFA_data.Rdata")
params <- readRDS("R_objects/animal_params.RDS")

# Test best method 
filt.test <- testDA(t(dat.SCFA), predictor = dat.clean$treatment, effectSize = 10, relative = FALSE, k = c(1,1,2))
## Warning in testDA(t(dat.SCFA), predictor = dat.clean$treatment, effectSize =
## 10, : Dataset contains very few features
## Running on 7 cores
## Warning in testDA(t(dat.SCFA), predictor = dat.clean$treatment, effectSize =
## 10, : Very few features spiked. Increase 'k' or set 'R' to more than 50 to
## ensure proper estimation of AUC and FPR
## predictor is assumed to be a categorical variable with 2 levels: CTRL, PFOS
## Spikeing...
## Testing 7 methods 20 times each...
##   |                                                                              |                                                                      |   0%  |                                                                              |                                                                      |   1%  |                                                                              |=                                                                     |   1%  |                                                                              |==                                                                    |   2%  |                                                                              |==                                                                    |   3%  |                                                                              |==                                                                    |   4%  |                                                                              |===                                                                   |   4%  |                                                                              |====                                                                  |   5%  |                                                                              |====                                                                  |   6%  |                                                                              |=====                                                                 |   7%  |                                                                              |======                                                                |   8%  |                                                                              |======                                                                |   9%  |                                                                              |=======                                                               |  10%  |                                                                              |========                                                              |  11%  |                                                                              |========                                                              |  12%  |                                                                              |=========                                                             |  13%  |                                                                              |==========                                                            |  14%  |                                                                              |==========                                                            |  15%  |                                                                              |===========                                                           |  16%  |                                                                              |============                                                          |  16%  |                                                                              |============                                                          |  17%  |                                                                              |============                                                          |  18%  |                                                                              |=============                                                         |  19%  |                                                                              |==============                                                        |  19%  |                                                                              |==============                                                        |  20%  |                                                                              |==============                                                        |  21%  |                                                                              |===============                                                       |  21%  |                                                                              |================                                                      |  22%  |                                                                              |================                                                      |  23%  |                                                                              |================                                                      |  24%  |                                                                              |=================                                                     |  24%  |                                                                              |==================                                                    |  25%  |                                                                              |==================                                                    |  26%  |                                                                              |===================                                                   |  27%  |                                                                              |====================                                                  |  28%  |                                                                              |====================                                                  |  29%  |                                                                              |=====================                                                 |  30%  |                                                                              |======================                                                |  31%  |                                                                              |======================                                                |  32%  |                                                                              |=======================                                               |  33%  |                                                                              |========================                                              |  34%  |                                                                              |========================                                              |  35%  |                                                                              |=========================                                             |  36%  |                                                                              |==========================                                            |  36%  |                                                                              |==========================                                            |  37%  |                                                                              |==========================                                            |  38%  |                                                                              |===========================                                           |  39%  |                                                                              |============================                                          |  39%  |                                                                              |============================                                          |  40%  |                                                                              |============================                                          |  41%  |                                                                              |=============================                                         |  41%  |                                                                              |==============================                                        |  42%  |                                                                              |==============================                                        |  43%  |                                                                              |==============================                                        |  44%  |                                                                              |===============================                                       |  44%  |                                                                              |================================                                      |  45%  |                                                                              |================================                                      |  46%  |                                                                              |=================================                                     |  47%  |                                                                              |==================================                                    |  48%  |                                                                              |==================================                                    |  49%  |                                                                              |===================================                                   |  50%  |                                                                              |====================================                                  |  51%  |                                                                              |====================================                                  |  52%  |                                                                              |=====================================                                 |  53%  |                                                                              |======================================                                |  54%  |                                                                              |======================================                                |  55%  |                                                                              |=======================================                               |  56%  |                                                                              |========================================                              |  56%  |                                                                              |========================================                              |  57%  |                                                                              |========================================                              |  58%  |                                                                              |=========================================                             |  59%  |                                                                              |==========================================                            |  59%  |                                                                              |==========================================                            |  60%  |                                                                              |==========================================                            |  61%  |                                                                              |===========================================                           |  61%  |                                                                              |============================================                          |  62%  |                                                                              |============================================                          |  63%  |                                                                              |============================================                          |  64%  |                                                                              |=============================================                         |  64%  |                                                                              |==============================================                        |  65%  |                                                                              |==============================================                        |  66%  |                                                                              |===============================================                       |  67%  |                                                                              |================================================                      |  68%  |                                                                              |================================================                      |  69%  |                                                                              |=================================================                     |  70%  |                                                                              |==================================================                    |  71%  |                                                                              |==================================================                    |  72%  |                                                                              |===================================================                   |  73%  |                                                                              |====================================================                  |  74%  |                                                                              |====================================================                  |  75%  |                                                                              |=====================================================                 |  76%  |                                                                              |======================================================                |  76%  |                                                                              |======================================================                |  77%  |                                                                              |======================================================                |  78%  |                                                                              |=======================================================               |  79%  |                                                                              |========================================================              |  79%  |                                                                              |========================================================              |  80%  |                                                                              |========================================================              |  81%  |                                                                              |=========================================================             |  81%  |                                                                              |==========================================================            |  82%  |                                                                              |==========================================================            |  83%  |                                                                              |==========================================================            |  84%  |                                                                              |===========================================================           |  84%  |                                                                              |============================================================          |  85%  |                                                                              |============================================================          |  86%  |                                                                              |=============================================================         |  87%  |                                                                              |==============================================================        |  88%  |                                                                              |==============================================================        |  89%  |                                                                              |===============================================================       |  90%  |                                                                              |================================================================      |  91%  |                                                                              |================================================================      |  92%  |                                                                              |=================================================================     |  93%  |                                                                              |==================================================================    |  94%  |                                                                              |==================================================================    |  95%  |                                                                              |===================================================================   |  96%  |                                                                              |====================================================================  |  96%  |                                                                              |====================================================================  |  97%  |                                                                              |====================================================================  |  98%  |                                                                              |===================================================================== |  99%  |                                                                              |======================================================================|  99%  |                                                                              |======================================================================| 100%
# Evaluate the plot and summary table
sum.fil <- summary(filt.test)
##               Method  AUC  FPR  FDR Power Score Score.5% Score.95%  
##          LIMMA (lim) 1.00 0.00 0.00  0.88  0.44     0.03      0.50 *
##         t-test (ttt) 1.00 0.00 0.00  0.88  0.44    -0.11      0.50 *
##     Log t-test (ltt) 1.00 0.00 0.00  0.75  0.38     0.03      0.50 *
##      Log LIMMA (lli) 1.00 0.00 0.00  0.75  0.38    -0.06      0.50 *
##         Wilcox (wil) 0.94 0.00 0.00  0.75  0.33    -0.06      0.50 *
##    Permutation (per) 1.00 0.00 0.29  1.00  0.21    -0.12      0.50 *
##  t-test - Rank (ttr) 0.59 0.25 0.33  0.50 -0.29    -0.53      0.08
p.fil <- plot(filt.test)
## Warning: The `fun.y` argument of `stat_summary()` is deprecated as of ggplot2 3.3.0.
## ℹ Please use the `fun` argument instead.
## ℹ The deprecated feature was likely used in the DAtest package.
##   Please report the issue at <https://github.com/Russel88/DAtest/issues>.
## This warning is displayed once every 8 hours.
## Call `lifecycle::last_lifecycle_warnings()` to see where this warning was
## generated.
## Warning: The `fun.ymin` argument of `stat_summary()` is deprecated as of ggplot2 3.3.0.
## ℹ Please use the `fun.min` argument instead.
## ℹ The deprecated feature was likely used in the DAtest package.
##   Please report the issue at <https://github.com/Russel88/DAtest/issues>.
## This warning is displayed once every 8 hours.
## Call `lifecycle::last_lifecycle_warnings()` to see where this warning was
## generated.
## Warning: The `fun.ymax` argument of `stat_summary()` is deprecated as of ggplot2 3.3.0.
## ℹ Please use the `fun.max` argument instead.
## ℹ The deprecated feature was likely used in the DAtest package.
##   Please report the issue at <https://github.com/Russel88/DAtest/issues>.
## This warning is displayed once every 8 hours.
## Call `lifecycle::last_lifecycle_warnings()` to see where this warning was
## generated.
p.fil

# Run DAtest
DA.ttt(t(dat.SCFA), dat.clean$feed)
##                      pval     pval.adj     log2FC ordering      Feature
## acetic       8.758125e-05 1.751625e-04  0.2041175    LF>HF       acetic
## formic       1.932949e-03 2.577265e-03  2.8436207    LF>HF       formic
## propanoic    2.059501e-02 2.353715e-02  0.3332647    LF>HF    propanoic
## m2_propanoic 4.067214e-09 3.253772e-08  2.0440321    LF>HF m2_propanoic
## butanoic     5.800573e-07 1.546819e-06 -1.1549635    HF>LF     butanoic
## m3_butanoic  8.629517e-04 1.380723e-03  1.1528124    LF>HF  m3_butanoic
## pentanoic    2.867293e-07 1.146917e-06  1.0109630    LF>HF    pentanoic
## hexanoic     2.923496e-02 2.923496e-02 -1.0180949    HF>LF     hexanoic
##                    Method
## acetic       t-test (ttt)
## formic       t-test (ttt)
## propanoic    t-test (ttt)
## m2_propanoic t-test (ttt)
## butanoic     t-test (ttt)
## m3_butanoic  t-test (ttt)
## pentanoic    t-test (ttt)
## hexanoic     t-test (ttt)
DA.ttt(t(dat.SCFA), dat.clean$treatment)
##                   pval  pval.adj        log2FC  ordering      Feature
## acetic       0.9961145 0.9961145  0.0002715497 PFOS>CTRL       acetic
## formic       0.2636677 0.7677208  0.8157610082 PFOS>CTRL       formic
## propanoic    0.1809856 0.7677208 -0.1976392059 CTRL>PFOS    propanoic
## m2_propanoic 0.5496795 0.7677208  0.2072936476 PFOS>CTRL m2_propanoic
## butanoic     0.6717557 0.7677208  0.1006386092 PFOS>CTRL     butanoic
## m3_butanoic  0.2912375 0.7677208 -0.3550074801 CTRL>PFOS  m3_butanoic
## pentanoic    0.4290549 0.7677208  0.1663651415 PFOS>CTRL    pentanoic
## hexanoic     0.6446947 0.7677208 -0.2101378894 CTRL>PFOS     hexanoic
##                    Method
## acetic       t-test (ttt)
## formic       t-test (ttt)
## propanoic    t-test (ttt)
## m2_propanoic t-test (ttt)
## butanoic     t-test (ttt)
## m3_butanoic  t-test (ttt)
## pentanoic    t-test (ttt)
## hexanoic     t-test (ttt)
DA.per(t(dat.SCFA), dat.clean$feed) # permanova
##        Feature       pval       log2FC coverage ordering    pval.adj
## 1       acetic 0.00009999  0.076500398    10000    LF>HF 0.000200000
## 2       formic 0.00059994  0.024004178    10000    LF>HF 0.000799920
## 3    propanoic 0.02229777  0.046041178    10000    LF>HF 0.022297770
## 4 m2_propanoic 0.00010000  0.013428597    10000    LF>HF 0.000200000
## 5     butanoic 0.00010000 -0.190083591    10000    HF>LF 0.000200000
## 6  m3_butanoic 0.00019998  0.004952697    10000    LF>HF 0.000319968
## 7    pentanoic 0.00010000  0.012306019    10000    LF>HF 0.000200000
## 8     hexanoic 0.00449955 -0.001605899    10000    HF>LF 0.005142343
##              Method
## 1 Permutation (per)
## 2 Permutation (per)
## 3 Permutation (per)
## 4 Permutation (per)
## 5 Permutation (per)
## 6 Permutation (per)
## 7 Permutation (per)
## 8 Permutation (per)
DA.per(t(dat.SCFA), dat.clean$treatment) # permanova
##        Feature       pval       log2FC coverage  ordering  pval.adj
## 1       acetic 0.09959004  0.000101823     1000 PFOS>CTRL 0.1991801
## 2       formic 0.27357264  0.008562522    10000 PFOS>CTRL 0.3389375
## 3    propanoic 0.17998200 -0.027357826    10000 CTRL>PFOS 0.2879712
## 4 m2_propanoic 0.05499450  0.001556962     1000 PFOS>CTRL 0.1741159
## 5     butanoic 0.06529347  0.017511891     1000 PFOS>CTRL 0.1741159
## 6  m3_butanoic 0.29657034 -0.001588705    10000 CTRL>PFOS 0.3389375
## 7    pentanoic 0.42355764  0.002087528    10000 PFOS>CTRL 0.4235576
## 8     hexanoic 0.06359364 -0.000347513     1000 CTRL>PFOS 0.1741159
##              Method
## 1 Permutation (per)
## 2 Permutation (per)
## 3 Permutation (per)
## 4 Permutation (per)
## 5 Permutation (per)
## 6 Permutation (per)
## 7 Permutation (per)
## 8 Permutation (per)

Best tests for data: t-tests and PERMANOVA Significant impact from feed was detected for all compounds, but no difference for treatment in any testing method.

8.3.2 PERMANOVA and PCA (visualization)

# Load data
load("R_objects/SCFA_data.Rdata")

# Calculating scaled Principal Component Analysis
pca.scfa <- prcomp(as.matrix(dat.SCFA), scale. = TRUE)

pca.scfa$rotation <- -1*pca.scfa$rotation
pca.scfa$x <- -1*pca.scfa$x

pca.scfa$rotation
##                     PC1       PC2         PC3         PC4         PC5
## acetic       -0.4569132 0.3220678 -0.29649356  0.16085706  0.09802827
## formic        0.2920027 0.1107856 -0.25218344  0.69629231  0.53699070
## propanoic    -0.3037349 0.1521728 -0.70293429 -0.27058879  0.04636900
## m2_propanoic  0.4539164 0.4107624  0.06023084  0.03348650 -0.22514426
## butanoic     -0.4376758 0.3179732  0.33195864  0.09486100 -0.10140800
## m3_butanoic   0.2375204 0.3287457  0.10109655 -0.61885033  0.61798278
## pentanoic     0.2165469 0.6275817 -0.11503873  0.04556649 -0.41784432
## hexanoic     -0.3358589 0.2984580  0.46593964  0.14460893  0.28707358
##                      PC6          PC7         PC8
## acetic        0.24697571  0.652250172  0.27871565
## formic        0.10087584 -0.208660135 -0.10939617
## propanoic    -0.35153145 -0.266106002 -0.34744323
## m2_propanoic -0.22793307  0.482538661 -0.53385209
## butanoic      0.48613748 -0.287535501 -0.50877100
## m3_butanoic   0.23835737 -0.005997736  0.05967541
## pentanoic     0.04072268 -0.377214468  0.47475840
## hexanoic     -0.67712407 -0.048696062  0.12973278
pca.scfa$x
##            PC1         PC2          PC3         PC4          PC5         PC6
## R01  1.6216522 -0.79840794 -0.010067003  0.91696195  0.912782382 -0.08189063
## R02  0.9530617 -0.33960856  0.147663930 -0.60712114 -0.447316304 -0.23596848
## R03 -0.2945006 -3.50171191  0.272491401 -0.11793283  0.152158657 -0.72878282
## R05  0.4980408 -1.69850661  0.795591257 -2.77972432  3.119713136  0.79413160
## R06  1.3428441  0.73876008 -0.454105736 -0.15050800 -0.300043429 -0.25192081
## R07  1.0066251  0.92773004 -0.400906330 -0.58890155 -0.975721516 -0.25030890
## R08  1.5858044  0.92661669  0.080499011 -0.96428297 -0.469644604 -0.17080287
## R09  1.3691320  0.78115275 -0.006498108 -1.01462081 -0.374971149 -0.21316744
## R10  0.6630036  0.21690845  0.542221597 -0.59121635 -0.365841548  0.07830816
## R11  1.7022801  0.30198959  0.323435182 -1.14228227 -0.182286183 -0.31069436
## R12  2.7847962  1.12902016  0.126601501  0.74683107  0.877559185  0.23207837
## R13  0.8294125 -1.04209568 -0.218549325  2.05077831  0.261463553  0.11173428
## R14  1.2425609  0.09544407 -0.247839208  0.55581434 -0.183144181 -0.07069110
## R15  1.0458655 -1.73121428  0.692922088  0.18365571 -1.070590831 -0.76036064
## R16  1.1959990 -0.39357029  0.176990527 -0.93476725 -0.200384715 -0.41769332
## R17  0.6535681 -0.53536988 -0.077395277  0.33382300 -1.388625628 -0.60205758
## R18  1.7328554 -0.17953567 -0.091888055  1.86208351 -0.501191397 -0.38249117
## R19  2.2411043  0.52081910 -0.401894736  1.91231895  1.141046088  0.29863420
## R20  2.5803655  0.18533610  0.547827210 -0.62109222  0.575387171 -0.24800750
## R21  1.7450516  1.81647795 -1.520088882  2.09828441  1.121592642  0.31010316
## R22  1.2351578 -0.66042771  0.750721267 -0.57156868 -0.349501701 -0.51549389
## R23  2.1094778  1.72228520 -0.471519274  0.24126633 -0.206480915 -0.12358708
## R24  2.2134833  1.06998868 -0.049602824 -0.16174938 -0.012531511 -0.19565891
## R25 -0.2174328 -0.36159486  0.004896641 -0.61637773 -0.281985349  0.20943545
## R26 -0.4499634  0.38706081 -2.329295584 -1.29448009 -0.375117122 -1.29244188
## R27 -1.8315064 -0.82590163 -1.262797380 -0.31683562  0.057068017 -0.41382335
## R28 -0.9910201  0.02520939  1.085927932  0.02355713 -0.107896407  0.59643158
## R29 -1.9481932  0.85037507 -1.421817522 -0.42556951 -0.115815465 -0.18956538
## R30 -1.0484581 -1.15869382 -0.941806728  2.21881808  1.223997969  0.49815788
## R31 -1.4034418  0.19289525 -2.511813257 -0.26621217  0.259745099 -0.25496752
## R32 -1.3518931  0.70975263  0.020095737 -0.51927479 -0.004660989  1.22601443
## R33 -0.4306888  1.17566924  0.232614203 -0.54258270 -0.405538144  1.05202492
## R34 -4.6236994  3.15251220  0.227924751  0.28037357  1.232773210 -0.90878879
## R35 -1.5311236 -0.27928619 -1.345500652 -0.40475432 -0.167019907  0.18729551
## R36 -2.4466967  1.28635709  3.494307030  1.57107194 -0.060964879 -1.66529731
## R37 -1.6259406 -0.17805050  1.052468580  0.41025591 -0.544931592  1.31684568
## R38 -1.0205859 -0.82240644  1.011858138  0.05011188 -0.270305021  1.30762316
## R39 -2.2490465 -0.70931765 -0.097586672  0.69577116 -0.502357666  1.18516726
## R40  0.4792411 -3.85145548  1.750003086  0.25627926  0.122780360 -0.09215686
## R41 -2.3385858  1.52527541  1.794725476 -0.42492983  1.191504144 -1.33514545
## R42 -1.8688355 -0.35644798 -0.411538288  0.27681190 -0.978575166  0.67120018
## R43  0.3578997  1.46878703  0.536471335 -1.06708362 -0.168799310  1.04659492
## R44 -1.6242760 -0.95661682 -1.272809476 -0.15333188 -0.201863570 -0.04402847
## R45  0.5365755  1.98275866  1.026253211 -0.84950440 -0.302302148  0.91264811
## R46 -1.1957354 -1.92226391 -0.583180879  0.18499246 -0.195019004 -0.14560301
## R47 -1.5783080  0.61584438  0.574607073  0.83878140 -1.124285114  0.48689756
## R48 -1.6559268 -1.50254220 -1.140616965 -0.58193785  0.586140851 -0.61993092
##               PC7          PC8
## R01 -0.0005809426 -0.129087269
## R02  0.0171989993  0.429005530
## R03 -0.1452228136 -0.479416641
## R05  0.4641861041  0.123038901
## R06 -0.0962599980  0.157811744
## R07 -0.0123408654  0.685307486
## R08  0.4768856494  0.355475933
## R09  0.4192727682  0.045518668
## R10  0.0026118322  0.127602282
## R11  0.2878649755 -0.017062428
## R12 -0.0128094026 -0.141179355
## R13  0.0993528243  0.354837683
## R14 -0.0293386139  0.562402328
## R15 -0.0583561648 -0.355207240
## R16  0.0028549686 -0.058320809
## R17  0.4313125319  0.191108927
## R18  0.5900724267 -0.242975159
## R19 -0.5881890910 -0.002172169
## R20 -0.1156138552 -0.363173500
## R21 -0.1565918020 -0.193466652
## R22 -0.0735653429  0.088697380
## R23  0.2284546940  0.090367513
## R24 -0.1088241664 -0.137771669
## R25 -0.4893907827 -0.258845613
## R26 -0.4113465018 -1.047455781
## R27 -0.0626530466  0.067140086
## R28  0.0135094505 -0.098751014
## R29  0.1661000579  0.049587367
## R30  0.0882575160 -0.072963193
## R31 -0.7112010281  0.337749505
## R32  0.3038533561 -0.833327789
## R33  0.1328344048 -0.342867992
## R34  1.1475584020  0.014208472
## R35 -0.0833786625  0.547401760
## R36  0.0158815286 -0.268977514
## R37 -0.5459921691  0.125304154
## R38 -0.4662016177  0.077296066
## R39  0.6516563652  0.195679135
## R40  0.1429796023  0.100613724
## R41 -1.3506099762  0.534010957
## R42 -0.8905609549 -0.244932035
## R43 -0.2125186879 -0.164162584
## R44  0.1217644205  0.269895782
## R45 -0.0745691957  0.047106009
## R46  0.4449809220  0.322929002
## R47  0.2493925646 -0.240755794
## R48  0.1972793165 -0.207224192
# Inspect result as biplot
biplot(pca.scfa, scale = 0)

# Calculate explained variance in each principal component
varex <- data.frame("PC" = 1:8, "variance" = pca.scfa$sdev^2 / sum(pca.scfa$sdev^2))
var.plot <- ggplot(varex, aes(x = PC, y = variance)) +
  geom_line() +
  geom_point() +
  xlab("PC") + ylab("Variance explained") + ylim(0,1)
var.plot

# Prepare data for plotting
mds.samples <- data.frame(pca.scfa$x) 
mds.scfa <- data.frame(pca.scfa$rotation)
mds.scfa$label1 <- row.names(pca.scfa$rotation)

# Renaming compounds to common name
mds.scfa$label2 <- case_when(mds.scfa$label1 == "formic" ~ "Formate", mds.scfa$label1 == "acetic" ~ "Acetate", mds.scfa$label1 == "propanoic" ~ "Propionate", mds.scfa$label1 == "m2_propanoic" ~ "Isobutyrate", mds.scfa$label1 == "butanoic" ~ "Butyrate", mds.scfa$label1 == "m3_butanoic" ~ "Isovalerate",mds.scfa$label1 == "pentanoic" ~ "Valerate", mds.scfa$label1 == "m4_pentanoic" ~ "Isocaproate", mds.scfa$label1 == "hexanoic" ~ "Caproate", mds.scfa$label1 == "heptanoic" ~ "Enanthate")

# Setting stating point for arrows
mds.scfa$x <- 0
mds.scfa$y <- 0

# Bind PCA data with main data
dat.mds <- cbind(dat.clean, mds.samples)

# PERMANOVA
perm <- adonis2(dat.SCFA ~ feed*treatment*dissection, data = dat.mds, permutations = 999, na.action = na.omit)
perm
## Permutation test for adonis under reduced model
## Terms added sequentially (first to last)
## Permutation: free
## Number of permutations: 999
## 
## adonis2(formula = dat.SCFA ~ feed * treatment * dissection, data = dat.mds, permutations = 999, na.action = na.omit)
##                           Df SumOfSqs      R2       F Pr(>F)    
## feed                       1  0.83364 0.43837 36.1803  0.001 ***
## treatment                  1  0.03172 0.01668  1.3765  0.256    
## dissection                 1  0.04702 0.02472  2.0405  0.123    
## feed:treatment             1  0.02490 0.01309  1.0805  0.309    
## feed:dissection            1  0.00550 0.00289  0.2388  0.833    
## treatment:dissection       1  0.03474 0.01827  1.5079  0.198    
## feed:treatment:dissection  1  0.02557 0.01345  1.1098  0.304    
## Residual                  39  0.89861 0.47253                   
## Total                     46  1.90170 1.00000                   
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1

8.3.3 Create figure

# Create plot
p <- ggplot() +
  geom_point(data = dat.mds, mapping = aes(x = PC1, y = PC2, color = feedtreat, shape = dissection, group = feedtreat), size = 2) +
  stat_ellipse(data = dat.mds, mapping = aes(x = PC1, y = PC2, color = feedtreat, fill = feedtreat), geom = "polygon", alpha = 0.1) +
  geom_segment(data = mds.scfa, mapping = aes(x=x, y=y, xend=3*PC1, yend=3*PC2), 
               lineend = "butt",
               linejoin = "round",
               size = 0.5,
               arrow = arrow(length = unit(0.2, 'cm'))) +
  geom_label_repel(data = mds.scfa, 
             mapping = aes(x = 3*PC1, y =3*PC2),
             label = mds.scfa$label2,
             size = 4,
             min.segment.length = 0,
             segment.alpha = 0.8,
             box.padding = 0.5,
             force = 1) +
  theme_pubr(legend = "right") +
  scale_color_manual(values = params$COL1, name = "Feed & treatment", labels = c("HF_CTRL" = "HF", "HF_PFOS" = "HF + PFOS","LF_CTRL" = "LF","LF_PFOS" = "LF + PFOS")) +
  scale_shape_manual(values = c("d08" = 19, "d12" = 1, "d16" = 17, "d21" = 2), name = "Day", labels = c("d08" = "8","d21" = "21")) +
  scale_fill_manual(values = params$COL1) +
  labs(x = "PC1", y = "PC2") +
  guides(fill = "none")
p

# Remove legend
leg <- get_legend(p)
## Warning in get_plot_component(plot, "guide-box"): Multiple components found;
## returning the first one. To return all, use `return_all = TRUE`.
p2 <- p + theme(legend.position = "none")

# Add marginal boxplots
p3 <- ggExtra::ggMarginal(p = p2, type = "boxplot", size = 10, groupFill = TRUE)
p3

# Add legend back to plot
p4 <- plot_grid(p3, leg, ncol = 2, rel_heights = 1, rel_widths = c(0.8,0.2))
p4

# Print permanova results
perm
## Permutation test for adonis under reduced model
## Terms added sequentially (first to last)
## Permutation: free
## Number of permutations: 999
## 
## adonis2(formula = dat.SCFA ~ feed * treatment * dissection, data = dat.mds, permutations = 999, na.action = na.omit)
##                           Df SumOfSqs      R2       F Pr(>F)    
## feed                       1  0.83364 0.43837 36.1803  0.001 ***
## treatment                  1  0.03172 0.01668  1.3765  0.256    
## dissection                 1  0.04702 0.02472  2.0405  0.123    
## feed:treatment             1  0.02490 0.01309  1.0805  0.309    
## feed:dissection            1  0.00550 0.00289  0.2388  0.833    
## treatment:dissection       1  0.03474 0.01827  1.5079  0.198    
## feed:treatment:dissection  1  0.02557 0.01345  1.1098  0.304    
## Residual                  39  0.89861 0.47253                   
## Total                     46  1.90170 1.00000                   
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
# Save output
suppressMessages(ggsave(filename = "plots/animal_data/scfa/PCOA_weighed.png", plot = p3, device = "png", dpi = 300, height = 130, width = 130, units = "mm"))
suppressMessages(ggsave(filename = "plots/animal_data/scfa/PCOA_weighed.pdf", plot = p3, device = "pdf", dpi = 300, height = 130, width = 130, units = "mm"))

# clear the environment and release memory
rm(list = ls(all.names = TRUE))
invisible(gc())

9 PH DATA

Here we examine pH data recorded from 1:5 dilutions of gastrointestinal content from upper and lower jejunum, ileum, and cecum.

9.1 Prepare data

# load data 
load("R_objects/animal_data.Rdata")
params <- readRDS("R_objects/animal_params.RDS")

# Isolate pH data and pivor longer
dat.clean <- dat %>% select(rat_name, treatment, feed, feedtreat, feedtreatday, dissection, pH_je_up, pH_je_down, pH_ileum, pH_cecum) %>% pivot_longer(., cols = c(pH_je_up, pH_je_down, pH_ileum, pH_cecum), names_to = "group", values_to = "pH")

# Remove rows with NA
dat.clean <- subset(dat.clean, !is.na(pH))

for (i in dat.clean$rat_name) {
  dat.clean$grfeed <- paste0(dat.clean$group,"_",dat.clean$feed)
  dat.clean$grday <- paste0(dat.clean$group,"_",dat.clean$dissection)
  dat.clean$feedday <-paste0(dat.clean$feed,"_",dat.clean$dissection)
}

df_order <- c("pH_je_up","pH_je_down","pH_ileum","pH_cecum")
dat.clean$group <- factor(as.character(dat.clean$group), levels = df_order)

# Set names of variables
PREDICTOR <- "grfeed"
OUTCOME <- "pH"
SUBJECT <- "rat_name"

# Create formula
PREDICTOR.F <- ifelse(length(PREDICTOR) > 1, paste(PREDICTOR, collapse = "*"), PREDICTOR)
FORMULA <- as.formula(paste(OUTCOME,PREDICTOR.F, sep = " ~ "))

# Summary samples in groups
dat.clean %>% group_by(across(all_of(c("feed","grfeed","treatment")))) %>% get_summary_stats(!!sym(OUTCOME), type = "mean_sd")
## # A tibble: 16 × 7
##    treatment feed  grfeed        variable     n  mean    sd
##    <chr>     <chr> <chr>         <fct>    <dbl> <dbl> <dbl>
##  1 CTRL      HF    pH_cecum_HF   pH          12  6.41 0.404
##  2 PFOS      HF    pH_cecum_HF   pH          12  6.73 0.781
##  3 CTRL      HF    pH_ileum_HF   pH          12  7.97 0.411
##  4 PFOS      HF    pH_ileum_HF   pH          12  7.83 0.357
##  5 CTRL      HF    pH_je_down_HF pH          12  7.17 0.398
##  6 PFOS      HF    pH_je_down_HF pH          12  7.32 0.477
##  7 CTRL      HF    pH_je_up_HF   pH          12  6.98 0.239
##  8 PFOS      HF    pH_je_up_HF   pH          12  7.06 0.314
##  9 CTRL      LF    pH_cecum_LF   pH          11  8.65 0.186
## 10 PFOS      LF    pH_cecum_LF   pH          12  8.48 0.306
## 11 CTRL      LF    pH_ileum_LF   pH          12  8.16 0.359
## 12 PFOS      LF    pH_ileum_LF   pH          12  7.84 0.453
## 13 CTRL      LF    pH_je_down_LF pH          12  7.05 0.46 
## 14 PFOS      LF    pH_je_down_LF pH          12  7.24 0.276
## 15 CTRL      LF    pH_je_up_LF   pH          12  6.87 0.368
## 16 PFOS      LF    pH_je_up_LF   pH          12  6.96 0.384
dat.clean %>% group_by(across(all_of(c("feed","grfeed","treatment")))) %>% get_summary_stats(!!sym(OUTCOME), type = "full")
## # A tibble: 16 × 16
##    treatment feed  grfeed    variable     n   min   max median    q1    q3   iqr
##    <chr>     <chr> <chr>     <fct>    <dbl> <dbl> <dbl>  <dbl> <dbl> <dbl> <dbl>
##  1 CTRL      HF    pH_cecum… pH          12  6.02  7.42   6.26  6.12  6.62 0.5  
##  2 PFOS      HF    pH_cecum… pH          12  5.55  8.52   6.64  6.28  7.02 0.732
##  3 CTRL      HF    pH_ileum… pH          12  7.32  8.57   7.98  7.76  8.32 0.56 
##  4 PFOS      HF    pH_ileum… pH          12  7.27  8.32   7.89  7.56  8.02 0.463
##  5 CTRL      HF    pH_je_do… pH          12  6.65  8.04   7.26  6.88  7.35 0.465
##  6 PFOS      HF    pH_je_do… pH          12  6.48  7.96   7.36  6.98  7.68 0.697
##  7 CTRL      HF    pH_je_up… pH          12  6.73  7.45   6.88  6.83  7.08 0.247
##  8 PFOS      HF    pH_je_up… pH          12  6.63  7.68   7.04  6.82  7.22 0.392
##  9 CTRL      LF    pH_cecum… pH          11  8.24  8.94   8.68  8.57  8.76 0.19 
## 10 PFOS      LF    pH_cecum… pH          12  7.87  9.09   8.51  8.32  8.60 0.282
## 11 CTRL      LF    pH_ileum… pH          12  7.56  8.86   8.20  7.94  8.35 0.407
## 12 PFOS      LF    pH_ileum… pH          12  6.79  8.58   7.92  7.68  8.07 0.385
## 13 CTRL      LF    pH_je_do… pH          12  5.95  7.66   7.02  6.89  7.33 0.445
## 14 PFOS      LF    pH_je_do… pH          12  6.67  7.57   7.28  7.10  7.45 0.345
## 15 CTRL      LF    pH_je_up… pH          12  6.18  7.47   6.84  6.75  7.16 0.413
## 16 PFOS      LF    pH_je_up… pH          12  6.32  7.61   6.95  6.76  7.25 0.493
## # ℹ 5 more variables: mad <dbl>, mean <dbl>, sd <dbl>, se <dbl>, ci <dbl>
# Create plot
bxp <- dat.clean %>%
  ggboxplot(x = "group",#if_else(length(PREDICTOR) > 1, PREDICTOR[2],PREDICTOR[1]),
            y = OUTCOME,
            color = "feedtreat",#PREDICTOR[1],
            facet.by = "dissection",#if(length(PREDICTOR) == 3) PREDICTOR[3],
            palette = params$COL)
bxp

# Test for outliers
dat.clean %>% 
  group_by(across(all_of(PREDICTOR))) %>% 
  identify_outliers(!!sym(OUTCOME))
## # A tibble: 4 × 13
##   grfeed  rat_name treatment feed  feedtreat feedtreatday dissection group    pH
##   <chr>   <chr>    <chr>     <chr> <chr>     <chr>        <chr>      <fct> <dbl>
## 1 pH_cec… R40      PFOS      HF    HF_PFOS   HF_PFOS_d08  d08        pH_c…  8.52
## 2 pH_cec… R17      PFOS      LF    LF_PFOS   LF_PFOS_d08  d08        pH_c…  7.87
## 3 pH_ile… R14      PFOS      LF    LF_PFOS   LF_PFOS_d08  d08        pH_i…  6.79
## 4 pH_je_… R12      CTRL      LF    LF_CTRL   LF_CTRL_d21  d21        pH_j…  5.95
## # ℹ 4 more variables: grday <chr>, feedday <chr>, is.outlier <lgl>,
## #   is.extreme <lgl>
# Check normality
# Build the linear model
model  <- lm(FORMULA, data = dat.clean)
# Create a QQ plot of residuals
ggqqplot(residuals(model))

# Compute Shapiro-Wilk test of normality
shapiro_test(residuals(model))
## # A tibble: 1 × 3
##   variable         statistic p.value
##   <chr>                <dbl>   <dbl>
## 1 residuals(model)     0.974 0.00116
# Check the homogeneity of variances with Levene's test
dat.clean %>% levene_test(FORMULA)
## Warning in leveneTest.default(y = y, group = group, ...): group coerced to
## factor.
## # A tibble: 1 × 4
##     df1   df2 statistic      p
##   <int> <int>     <dbl>  <dbl>
## 1     7   183      2.16 0.0401
# Save result
EQUAL.VAR <- dat.clean %>% levene_test(FORMULA) %>% pull(p) > 0.05
## Warning in leveneTest.default(y = y, group = group, ...): group coerced to
## factor.
# Save dat.clean
save(dat.clean, model, EQUAL.VAR, FORMULA,OUTCOME,PREDICTOR,PREDICTOR.F,SUBJECT, file = "plots/animal_data/pH/ph_data.Rdata")

This shows that data has four outliers, is not normally distribution and does not have equal variance. Therefore we can test the data with a Kruskal-Wallis with posthoc Dunn’s test adjusted p-values.

9.2 Kruskal-Wallis test

Perform test

res.aov <- dat.clean %>% kruskal_test(FORMULA)
res.aov
## # A tibble: 1 × 6
##   .y.       n statistic    df        p method        
## * <chr> <int>     <dbl> <int>    <dbl> <chr>         
## 1 pH      191      130.     7 7.25e-25 Kruskal-Wallis

Effect size

The eta squared, based on the H-statistic, can be used as the measure of the Kruskal-Wallis test effect size. The interpretation values commonly in published literature are: 0.01- < 0.06 (small effect), 0.06 - < 0.14 (moderate effect) and >= 0.14 (large effect).

dat.clean %>% kruskal_effsize(FORMULA)
## # A tibble: 1 × 5
##   .y.       n effsize method  magnitude
## * <chr> <int>   <dbl> <chr>   <ord>    
## 1 pH      191   0.671 eta2[H] large

Post-hoc test if interaction is significant

A significant Kruskal-Wallis test is generally followed up by Dunn’s test to identify which groups are different. It’s also possible to use the Wilcoxon’s test to calculate pairwise comparisons between group levels with corrections for multiple testing.

# pairwise comparisons
pwc <- dat.clean %>% 
  dunn_test(FORMULA, p.adjust.method = "fdr") 
pwc
## # A tibble: 28 × 9
##    .y.   group1      group2    n1    n2 statistic        p    p.adj p.adj.signif
##  * <chr> <chr>       <chr>  <int> <int>     <dbl>    <dbl>    <dbl> <chr>       
##  1 pH    pH_cecum_HF pH_ce…    24    23      8.72 2.79e-18 7.82e-17 ****        
##  2 pH    pH_cecum_HF pH_il…    24    24      6.39 1.64e-10 9.18e-10 ****        
##  3 pH    pH_cecum_HF pH_il…    24    24      6.80 1.08e-11 7.57e-11 ****        
##  4 pH    pH_cecum_HF pH_je…    24    24      3.19 1.44e- 3 2.52e- 3 **          
##  5 pH    pH_cecum_HF pH_je…    24    24      2.83 4.67e- 3 7.69e- 3 **          
##  6 pH    pH_cecum_HF pH_je…    24    24      1.86 6.30e- 2 8.82e- 2 ns          
##  7 pH    pH_cecum_HF pH_je…    24    24      1.43 1.52e- 1 1.94e- 1 ns          
##  8 pH    pH_cecum_LF pH_il…    23    24     -2.40 1.66e- 2 2.58e- 2 *           
##  9 pH    pH_cecum_LF pH_il…    23    24     -2.00 4.58e- 2 6.75e- 2 ns          
## 10 pH    pH_cecum_LF pH_je…    23    24     -5.57 2.59e- 8 1.04e- 7 ****        
## # ℹ 18 more rows

Several groups show significant difference based of compartment, feed and day of sampling. We will plot this as a nested analysis with feed and day as a combined inner variable and compartment (group) as the outer variable, using kruskal-wallis with posthoc Dunn’s test for both.

9.3 Create figure (day)

# ## Comparison for inner variable
stat.in <- dat.clean %>%
  group_by(dissection,group) %>%
  wilcox_test(pH ~ feedday) %>%
  adjust_pvalue(method = "BH") %>%
  add_significance("p.adj") %>%
  add_xy_position(x = "group", dodge = 0.8) %>%
  p_format("p.adj", accuracy = 0.001, trailing.zero = TRUE, new.col = TRUE)
stat.in
## # A tibble: 8 × 17
##   dissection group     .y.   group1 group2    n1    n2 statistic       p   p.adj
## * <chr>      <fct>     <chr> <chr>  <chr>  <int> <int>     <dbl>   <dbl>   <dbl>
## 1 d08        pH_je_up  pH    HF_d08 LF_d08    12    12      65   7.07e-1 8.08e-1
## 2 d08        pH_je_do… pH    HF_d08 LF_d08    12    12      71   9.77e-1 9.77e-1
## 3 d08        pH_ileum  pH    HF_d08 LF_d08    12    12      59   4.78e-1 6.51e-1
## 4 d08        pH_cecum  pH    HF_d08 LF_d08    12    11       6   2.49e-4 9.96e-4
## 5 d21        pH_je_up  pH    HF_d21 LF_d21    12    12     109   3.5 e-2 9.33e-2
## 6 d21        pH_je_do… pH    HF_d21 LF_d21    12    12      84.5 4.88e-1 6.51e-1
## 7 d21        pH_ileum  pH    HF_d21 LF_d21    12    12      59   4.7 e-1 6.51e-1
## 8 d21        pH_cecum  pH    HF_d21 LF_d21    12    12       0   7.4 e-7 5.92e-6
## # ℹ 7 more variables: p.adj.signif <chr>, y.position <dbl>,
## #   groups <named list>, x <dbl>, xmin <dbl>, xmax <dbl>, p.adj.format <chr>
## Comparison for outer variable
stat.out <- dat.clean %>%
  group_by(dissection) %>%
  kruskal_test(pH ~ group) %>%
  add_significance() %>%
  p_format("p", accuracy = 0.001, trailing.zero = TRUE, new.col = TRUE)
stat.out
## # A tibble: 2 × 9
##   dissection .y.       n statistic    df           p method    p.signif p.format
## * <chr>      <chr> <int>     <dbl> <int>       <dbl> <chr>     <chr>    <chr>   
## 1 d08        pH       95      20.7     3 0.000121    Kruskal-… ***      <0.001  
## 2 d21        pH       96      33.5     3 0.000000249 Kruskal-… ****     <0.001
pwc1 <- dat.clean %>%
  group_by(dissection) %>%
  dunn_test(pH ~ group) %>%
  add_significance() %>%
  p_format("p.adj", accuracy = 0.001, trailing.zero = TRUE, new.col = TRUE) %>%
  add_xy_position(x = "group", dodge = 0.8)
pwc1
## # A tibble: 12 × 15
##    dissection .y.   group1     group2        n1    n2 statistic        p   p.adj
##    <chr>      <chr> <chr>      <chr>      <int> <int>     <dbl>    <dbl>   <dbl>
##  1 d08        pH    pH_je_up   pH_je_down    24    24     0.406  6.85e-1 6.85e-1
##  2 d08        pH    pH_je_up   pH_ileum      24    24     4.06   4.84e-5 2.91e-4
##  3 d08        pH    pH_je_up   pH_cecum      24    23     2.15   3.17e-2 1.27e-1
##  4 d08        pH    pH_je_down pH_ileum      24    24     3.66   2.55e-4 1.27e-3
##  5 d08        pH    pH_je_down pH_cecum      24    23     1.75   8.08e-2 1.84e-1
##  6 d08        pH    pH_ileum   pH_cecum      24    23    -1.87   6.12e-2 1.84e-1
##  7 d21        pH    pH_je_up   pH_je_down    24    24     2.37   1.79e-2 3.58e-2
##  8 d21        pH    pH_je_up   pH_ileum      24    24     5.73   9.84e-9 5.90e-8
##  9 d21        pH    pH_je_up   pH_cecum      24    24     3.16   1.56e-3 6.24e-3
## 10 d21        pH    pH_je_down pH_ileum      24    24     3.37   7.64e-4 3.82e-3
## 11 d21        pH    pH_je_down pH_cecum      24    24     0.795  4.26e-1 4.26e-1
## 12 d21        pH    pH_ileum   pH_cecum      24    24    -2.57   1.02e-2 3.05e-2
## # ℹ 6 more variables: p.adj.signif <chr>, p.adj.format <chr>, y.position <dbl>,
## #   groups <named list>, xmin <dbl>, xmax <dbl>
# Test between days
stat.out2 <- dat.clean %>%
  # group_by(feed) %>%
  kruskal_test(pH ~ grday) %>%
  add_significance() %>%
  p_format("p", accuracy = 0.001, trailing.zero = TRUE, new.col = TRUE)
stat.out
## # A tibble: 2 × 9
##   dissection .y.       n statistic    df           p method    p.signif p.format
## * <chr>      <chr> <int>     <dbl> <int>       <dbl> <chr>     <chr>    <chr>   
## 1 d08        pH       95      20.7     3 0.000121    Kruskal-… ***      <0.001  
## 2 d21        pH       96      33.5     3 0.000000249 Kruskal-… ****     <0.001
pwc2 <- dat.clean %>%
  # group_by(feed) %>%
  dunn_test(pH ~ grday) %>%
  add_significance() %>%
  p_format("p.adj", accuracy = 0.001, trailing.zero = TRUE, new.col = TRUE) %>%
  add_xy_position(x = "group", dodge = 0.8)
pwc2
## # A tibble: 28 × 14
##    .y.   group1       group2     n1    n2 statistic       p   p.adj p.adj.signif
##    <chr> <chr>        <chr>   <int> <int>     <dbl>   <dbl>   <dbl> <chr>       
##  1 pH    pH_cecum_d08 pH_cec…    23    24    -0.321 7.48e-1 1       ns          
##  2 pH    pH_cecum_d08 pH_ile…    23    24     1.88  5.95e-2 0.714   ns          
##  3 pH    pH_cecum_d08 pH_ile…    23    24     2.30  2.15e-2 0.318   ns          
##  4 pH    pH_cecum_d08 pH_je_…    23    24    -1.40  1.63e-1 1       ns          
##  5 pH    pH_cecum_d08 pH_je_…    23    24    -1.51  1.30e-1 1       ns          
##  6 pH    pH_cecum_d08 pH_je_…    23    24    -1.66  9.62e-2 1       ns          
##  7 pH    pH_cecum_d08 pH_je_…    23    24    -3.94  8.05e-5 0.00201 **          
##  8 pH    pH_cecum_d21 pH_ile…    24    24     2.23  2.58e-2 0.335   ns          
##  9 pH    pH_cecum_d21 pH_ile…    24    24     2.65  8.08e-3 0.145   ns          
## 10 pH    pH_cecum_d21 pH_je_…    24    24    -1.09  2.77e-1 1       ns          
## # ℹ 18 more rows
## # ℹ 5 more variables: p.adj.format <chr>, y.position <dbl>,
## #   groups <named list>, xmin <dbl>, xmax <dbl>
# Adjust x positions of inner statistics
stat.in$xmin <- c(0.8,1.8,2.8,3.8,0.8,1.8,2.8,3.8)
stat.in$xmax <- c(1.2,2.2,3.2,4.2,1.2,2.2,3.2,4.2)

# Create plot
p <- ggplot(dat.clean, aes(x = group, y = pH)) +
  geom_boxplot(aes(fill = feed, color = feed), outlier.shape = NA) +
  geom_point(aes(color = feed), position = position_jitterdodge(jitter.width = 0.15, dodge.width = 0.8), size = 0.8) +
  scale_color_manual(values = c("black","black","black","black")) +
  scale_fill_manual(values = params$COLFEED, name = "Feed") +
  facet_wrap(.~dissection, ncol = 2, nrow = 1, labeller = labeller("dissection" = c("d08" = "Day 8","d21" = "Day 21"))) +
  scale_y_continuous(name = "pH", breaks = seq(6,12,1), limits = c(5.5,10)) +
  scale_x_discrete(name = "Compartment", labels = c("pH_je_up" = "Jejunum\nUpper","pH_je_down" = "Jejunum\nLower","pH_ileum" = "Ileum","pH_cecum" = "Cecum")) +
  theme_pubr(legend = "top") +
  guides(color = "none", linetype = "none") +
  theme(axis.title.x = element_blank())
p

# Add statistics to plot
p.stat <- p + stat_pvalue_manual(stat.in, tip.length = 0, hide.ns = TRUE, color = "red") +
  stat_pvalue_manual(pwc1, tip.length = 0, hide.ns = TRUE, y.position = c(9,9.3,9,9.3,9.6,8.7,9.9))
p.stat

# Save plot
suppressMessages(ggsave(filename = "plots/animal_data/pH/pH_all_groups_v2.pdf", plot = p.stat, device = "pdf", dpi = 300, units = "mm", height = 100, width = 200))
suppressMessages(ggsave(filename = "plots/animal_data/pH/pH_all_groups_v2.png", plot = p.stat, device = "png", dpi = 300, units = "mm", height = 100, width = 200))

# clear the environment and release memory
rm(list = ls(all.names = TRUE)[ls(all.names = TRUE) != "params"])
invisible(gc())

9.4 Create figure (PFOS)

# Load data
load("plots/animal_data/pH/ph_data.Rdata")
## Comparison for inner variable
stat.in <- dat.clean %>%
  group_by(dissection,group) %>%
  kruskal_test(pH ~ feedtreat) %>%
  add_significance() %>%
  p_format("p", accuracy = 0.001, trailing.zero = TRUE, new.col = TRUE)
stat.in
## # A tibble: 8 × 10
##   dissection group  .y.       n statistic    df       p method p.signif p.format
## * <chr>      <fct>  <chr> <int>     <dbl> <int>   <dbl> <chr>  <chr>    <chr>   
## 1 d08        pH_je… pH       24     1.57      3 6.67e-1 Krusk… ns       0.6670  
## 2 d08        pH_je… pH       24     0.620     3 8.92e-1 Krusk… ns       0.8920  
## 3 d08        pH_il… pH       24     0.980     3 8.06e-1 Krusk… ns       0.8060  
## 4 d08        pH_ce… pH       23    14.6       3 2.21e-3 Krusk… **       0.0022  
## 5 d21        pH_je… pH       24     5.14      3 1.62e-1 Krusk… ns       0.1620  
## 6 d21        pH_je… pH       24     3.31      3 3.46e-1 Krusk… ns       0.3460  
## 7 d21        pH_il… pH       24     5.65      3 1.3 e-1 Krusk… ns       0.1300  
## 8 d21        pH_ce… pH       24    17.9       3 4.51e-4 Krusk… ***      <0.001
pwc1 <- dat.clean %>%
  group_by(dissection,group) %>%
  dunn_test(pH ~ feedtreat) %>%
  add_significance() %>%
  p_format("p.adj", accuracy = 0.001, trailing.zero = TRUE, new.col = TRUE)
pwc1
## # A tibble: 48 × 12
##    dissection group      .y.   group1  group2     n1    n2 statistic     p p.adj
##  * <chr>      <fct>      <chr> <chr>   <chr>   <int> <int>     <dbl> <dbl> <dbl>
##  1 d08        pH_je_up   pH    HF_CTRL HF_PFOS     6     6     0.817 0.414     1
##  2 d08        pH_je_up   pH    HF_CTRL LF_CTRL     6     6     0.265 0.791     1
##  3 d08        pH_je_up   pH    HF_CTRL LF_PFOS     6     6     1.12  0.261     1
##  4 d08        pH_je_up   pH    HF_PFOS LF_CTRL     6     6    -0.551 0.581     1
##  5 d08        pH_je_up   pH    HF_PFOS LF_PFOS     6     6     0.306 0.759     1
##  6 d08        pH_je_up   pH    LF_CTRL LF_PFOS     6     6     0.858 0.391     1
##  7 d08        pH_je_down pH    HF_CTRL HF_PFOS     6     6     0.776 0.438     1
##  8 d08        pH_je_down pH    HF_CTRL LF_CTRL     6     6     0.490 0.624     1
##  9 d08        pH_je_down pH    HF_CTRL LF_PFOS     6     6     0.367 0.713     1
## 10 d08        pH_je_down pH    HF_PFOS LF_CTRL     6     6    -0.286 0.775     1
## # ℹ 38 more rows
## # ℹ 2 more variables: p.adj.signif <chr>, p.adj.format <chr>
## Comparison for outer variable
stat.out <- dat.clean %>%
  group_by(dissection) %>%
  kruskal_test(pH ~ group) %>%
  add_significance() %>%
  p_format("p", accuracy = 0.001, trailing.zero = TRUE, new.col = TRUE)
stat.out
## # A tibble: 2 × 9
##   dissection .y.       n statistic    df           p method    p.signif p.format
## * <chr>      <chr> <int>     <dbl> <int>       <dbl> <chr>     <chr>    <chr>   
## 1 d08        pH       95      20.7     3 0.000121    Kruskal-… ***      <0.001  
## 2 d21        pH       96      33.5     3 0.000000249 Kruskal-… ****     <0.001
pwc2 <- dat.clean %>%
  group_by(dissection) %>%
  dunn_test(pH ~ group) %>%
  add_significance() %>%
  p_format("p.adj", accuracy = 0.001, trailing.zero = TRUE, new.col = TRUE)
pwc2
## # A tibble: 12 × 11
##    dissection .y.   group1     group2        n1    n2 statistic        p   p.adj
##  * <chr>      <chr> <chr>      <chr>      <int> <int>     <dbl>    <dbl>   <dbl>
##  1 d08        pH    pH_je_up   pH_je_down    24    24     0.406  6.85e-1 6.85e-1
##  2 d08        pH    pH_je_up   pH_ileum      24    24     4.06   4.84e-5 2.91e-4
##  3 d08        pH    pH_je_up   pH_cecum      24    23     2.15   3.17e-2 1.27e-1
##  4 d08        pH    pH_je_down pH_ileum      24    24     3.66   2.55e-4 1.27e-3
##  5 d08        pH    pH_je_down pH_cecum      24    23     1.75   8.08e-2 1.84e-1
##  6 d08        pH    pH_ileum   pH_cecum      24    23    -1.87   6.12e-2 1.84e-1
##  7 d21        pH    pH_je_up   pH_je_down    24    24     2.37   1.79e-2 3.58e-2
##  8 d21        pH    pH_je_up   pH_ileum      24    24     5.73   9.84e-9 5.90e-8
##  9 d21        pH    pH_je_up   pH_cecum      24    24     3.16   1.56e-3 6.24e-3
## 10 d21        pH    pH_je_down pH_ileum      24    24     3.37   7.64e-4 3.82e-3
## 11 d21        pH    pH_je_down pH_cecum      24    24     0.795  4.26e-1 4.26e-1
## 12 d21        pH    pH_ileum   pH_cecum      24    24    -2.57   1.02e-2 3.05e-2
## # ℹ 2 more variables: p.adj.signif <chr>, p.adj.format <chr>
## Calculate positions statistics on plot
pwc1 <- pwc1 %>% add_xy_position(x = "group", dodge = 0.8)
pwc2 <- pwc2 %>% add_xy_position(x = "group")

# Create plot 
p <- ggboxplot(dat.clean, x = "group", y = "pH",
               fill = "feedtreat",
               color = "feedtreat",
               add = "jitter",
               add.params = list(size = 0.8)) +
  theme_pubr(legend = "top") +
  scale_color_manual(values = c("black","black","black","black")) +
  scale_fill_manual(values = c("HF_d08" = "#d2e9cc","HF_d21" = "#32a248","LF_d08" = "#bde7fb","LF_d21" = "#1879b7"), name = "Feed per day", labels = c("HF Day 8","HF Day 21","LF Day 8","LF Day 21")) +
  scale_alpha_manual(values = c(1,0.5,1,0.5)) +
  scale_y_continuous(name = "pH", breaks = seq(6,12,1)) +
  scale_x_discrete(name = "Compartment", labels = c("pH_je_up" = "Jejunum\nUpper","pH_je_down" = "Jejunum\nLower","pH_ileum" = "Ileum","pH_cecum" = "Cecum")) +
  theme(axis.title.x = element_blank()) +
  guides(color = "none") +
  facet_wrap("dissection ~.", ncol = 2, nrow = 1)

p.stat <- p + stat_pvalue_manual(pwc1, tip.length = 0, hide.ns = TRUE, color = "red") +
  stat_pvalue_manual(pwc2, tip.length = 0, hide.ns = TRUE) 
p.stat
## Warning: No shared levels found between `names(values)` of the manual scale and the
## data's fill values.
## No shared levels found between `names(values)` of the manual scale and the
## data's fill values.

suppressMessages(ggsave(filename = "plots/animal_data/pH/pH_all_pfos.pdf", plot = p.stat, device = "pdf", dpi = 300, units = "mm", height = 100, width = 200))
## Warning: No shared levels found between `names(values)` of the manual scale and the
## data's fill values.
## No shared levels found between `names(values)` of the manual scale and the
## data's fill values.
suppressMessages(ggsave(filename = "plots/animal_data/pH/pH_all_pfos.png", plot = p.stat, device = "png", dpi = 300, units = "mm", height = 100, width = 200))
## Warning: No shared levels found between `names(values)` of the manual scale and the
## data's fill values.
## No shared levels found between `names(values)` of the manual scale and the
## data's fill values.
# clear the environment and release memory
rm(list = ls(all.names = TRUE)[ls(all.names = TRUE) != "params"])
invisible(gc())

9.5 Create figure (feed)

# Load data
load("plots/animal_data/pH/ph_data.Rdata")

## Comparison for inner variable
stat.in <- dat.clean %>%
  group_by(feed,group) %>%
  kruskal_test(pH ~ feedtreat) %>%
  add_significance() %>%
  p_format("p", accuracy = 0.001, trailing.zero = TRUE, new.col = TRUE)
stat.in
## # A tibble: 8 × 10
##   feed  group      .y.       n statistic    df      p method   p.signif p.format
## * <chr> <fct>      <chr> <int>     <dbl> <int>  <dbl> <chr>    <chr>    <chr>   
## 1 HF    pH_je_up   pH       24     0.214     1 0.644  Kruskal… ns       0.644   
## 2 HF    pH_je_down pH       24     0.853     1 0.356  Kruskal… ns       0.356   
## 3 HF    pH_ileum   pH       24     0.802     1 0.371  Kruskal… ns       0.371   
## 4 HF    pH_cecum   pH       24     1.20      1 0.273  Kruskal… ns       0.273   
## 5 LF    pH_je_up   pH       24     0.654     1 0.419  Kruskal… ns       0.419   
## 6 LF    pH_je_down pH       24     0.964     1 0.326  Kruskal… ns       0.326   
## 7 LF    pH_ileum   pH       24     3.00      1 0.0832 Kruskal… ns       0.083   
## 8 LF    pH_cecum   pH       23     3.30      1 0.0691 Kruskal… ns       0.069
pwc1 <- dat.clean %>%
  group_by(feed,group) %>%
  dunn_test(pH ~ feedtreat) %>%
  add_significance() %>%
  p_format("p.adj", accuracy = 0.001, trailing.zero = TRUE, new.col = TRUE)
pwc1
## # A tibble: 8 × 12
##   feed  group      .y.   group1  group2     n1    n2 statistic      p  p.adj
## * <chr> <fct>      <chr> <chr>   <chr>   <int> <int>     <dbl>  <dbl>  <dbl>
## 1 HF    pH_je_up   pH    HF_CTRL HF_PFOS    12    12     0.462 0.644  0.644 
## 2 HF    pH_je_down pH    HF_CTRL HF_PFOS    12    12     0.924 0.356  0.356 
## 3 HF    pH_ileum   pH    HF_CTRL HF_PFOS    12    12    -0.895 0.371  0.371 
## 4 HF    pH_cecum   pH    HF_CTRL HF_PFOS    12    12     1.10  0.273  0.273 
## 5 LF    pH_je_up   pH    LF_CTRL LF_PFOS    12    12     0.808 0.419  0.419 
## 6 LF    pH_je_down pH    LF_CTRL LF_PFOS    12    12     0.982 0.326  0.326 
## 7 LF    pH_ileum   pH    LF_CTRL LF_PFOS    12    12    -1.73  0.0832 0.0832
## 8 LF    pH_cecum   pH    LF_CTRL LF_PFOS    11    12    -1.82  0.0691 0.0691
## # ℹ 2 more variables: p.adj.signif <chr>, p.adj.format <chr>
## Comparison for outer variable
stat.out <- dat.clean %>%
  group_by(feed) %>%
  kruskal_test(pH ~ group) %>%
  add_significance() %>%
  p_format("p", accuracy = 0.001, trailing.zero = TRUE, new.col = TRUE)
stat.out
## # A tibble: 2 × 9
##   feed  .y.       n statistic    df        p method         p.signif p.format
## * <chr> <chr> <int>     <dbl> <int>    <dbl> <chr>          <chr>    <chr>   
## 1 HF    pH       96      52.4     3 2.49e-11 Kruskal-Wallis ****     <0.001  
## 2 LF    pH       95      71.6     3 1.97e-15 Kruskal-Wallis ****     <0.001
pwc2 <- dat.clean %>%
  group_by(feed) %>%
  dunn_test(pH ~ group) %>%
  add_significance() %>%
  p_format("p.adj", accuracy = 0.001, trailing.zero = TRUE, new.col = TRUE)
pwc2
## # A tibble: 12 × 11
##    feed  .y.   group1     group2        n1    n2 statistic        p    p.adj
##  * <chr> <chr> <chr>      <chr>      <int> <int>     <dbl>    <dbl>    <dbl>
##  1 HF    pH    pH_je_up   pH_je_down    24    24      1.43 1.53e- 1 1.53e- 1
##  2 HF    pH    pH_je_up   pH_ileum      24    24      4.74 2.15e- 6 1.08e- 5
##  3 HF    pH    pH_je_up   pH_cecum      24    24     -2.32 2.03e- 2 4.05e- 2
##  4 HF    pH    pH_je_down pH_ileum      24    24      3.31 9.29e- 4 2.79e- 3
##  5 HF    pH    pH_je_down pH_cecum      24    24     -3.75 1.78e- 4 7.10e- 4
##  6 HF    pH    pH_ileum   pH_cecum      24    24     -7.06 1.66e-12 9.98e-12
##  7 LF    pH    pH_je_up   pH_je_down    24    24      1.24 2.14e- 1 2.14e- 1
##  8 LF    pH    pH_je_up   pH_ileum      24    24      5.09 3.54e- 7 1.42e- 6
##  9 LF    pH    pH_je_up   pH_cecum      24    23      7.50 6.62e-14 3.97e-13
## 10 LF    pH    pH_je_down pH_ileum      24    24      3.85 1.19e- 4 3.57e- 4
## 11 LF    pH    pH_je_down pH_cecum      24    23      6.27 3.73e-10 1.86e- 9
## 12 LF    pH    pH_ileum   pH_cecum      24    23      2.46 1.40e- 2 2.80e- 2
## # ℹ 2 more variables: p.adj.signif <chr>, p.adj.format <chr>
## Calculate positions statistics on plot
pwc1 <- pwc1 %>% add_xy_position(x = "group", dodge = 0.8)
pwc2 <- pwc2 %>% add_xy_position(x = "group")

# Create plot
p <- ggboxplot(dat.clean, x = "group", y = "pH",
               fill = "feedtreatday",
               color = "feedtreatday",
               add = "jitter",
               add.params = list(size = 0.8)) +
  theme_pubr(legend = "top") +
  scale_color_manual(values = c("black","black","black","black","black","black","black","black")) +
  scale_alpha_manual(values = c(1,0.5,1,0.5)) +
  scale_y_continuous(name = "pH", breaks = seq(6,12,1)) +
  scale_x_discrete(name = "Compartment", labels = c("pH_je_up" = "Jejunum\nUpper","pH_je_down" = "Jejunum\nLower","pH_ileum" = "Ileum","pH_cecum" = "Cecum")) +
  theme(axis.title.x = element_blank()) +
  guides(color = "none") +
  facet_wrap("feed ~.", ncol = 2, nrow = 1)

p.stat <- p + stat_pvalue_manual(pwc1, tip.length = 0, hide.ns = TRUE, color = "red") + 
  stat_pvalue_manual(pwc2, tip.length = 0, hide.ns = TRUE) 
p.stat

suppressMessages(ggsave(filename = "plots/animal_data/pH/pH_all_pfos.pdf", plot = p.stat, device = "pdf", dpi = 300, units = "mm", height = 100, width = 200))
suppressMessages(ggsave(filename = "plots/animal_data/pH/pH_all_pfos.png", plot = p.stat, device = "png", dpi = 300, units = "mm", height = 100, width = 200))

# clear the environment and release memory
rm(list = ls(all.names = TRUE)[ls(all.names = TRUE) != "params"])
invisible(gc())

9.6 Conclusion

Measurements of pH in diluted GI content show significant variations between compartments in the GI with a generally increasing pH the further “down” the sample is taken. The largest feed-driven difference is observed in the caecal content, where pH is markedly lower in the HF group.

10 TRANSIT TIME DATA

10.1 Prepare data

# load data 
load("R_objects/animal_data.Rdata")
params <- readRDS("R_objects/animal_params.RDS")

#Isolate transit time data
dat.clean <- dat %>% select(rat_name, treatment, feed, feedtreat, dissection, cage, transit_0, transit_7, transit_20) %>% pivot_longer(., cols = c(transit_0, transit_7, transit_20), names_to = "transit", values_to = "min")

dat.clean <- dat.clean %>%
  mutate("day" = case_when(transit == "transit_0" ~ "d0",
                           transit == "transit_7" ~ "d07",
                           transit == "transit_20" ~ "d20"))
dat.clean <- subset(dat.clean, !is.na(min))

# Set names of variables
PREDICTOR <- c("day","feedtreat")#"day"
OUTCOME <- "min"
SUBJECT <- "rat_name"

# Create formula
PREDICTOR.F <- ifelse(length(PREDICTOR) > 1, paste(PREDICTOR, collapse = "*"), PREDICTOR)
FORMULA <- as.formula(paste(OUTCOME,PREDICTOR.F, sep = " ~ "))

# Summary samples in groups
dat.clean %>% group_by(across(all_of(PREDICTOR))) %>% get_summary_stats(!!sym(OUTCOME), type = "mean_sd")
## # A tibble: 12 × 6
##    feedtreat day   variable     n  mean    sd
##    <chr>     <chr> <fct>    <dbl> <dbl> <dbl>
##  1 HF_CTRL   d0    min          6  512. 163. 
##  2 HF_PFOS   d0    min          5  504  109. 
##  3 LF_CTRL   d0    min          6  562. 134. 
##  4 LF_PFOS   d0    min          6  589. 121. 
##  5 HF_CTRL   d07   min         12  774. 164. 
##  6 HF_PFOS   d07   min         12  702. 145. 
##  7 LF_CTRL   d07   min         12  851. 113. 
##  8 LF_PFOS   d07   min         12  846. 118. 
##  9 HF_CTRL   d20   min          6  853.  93.5
## 10 HF_PFOS   d20   min          6  910   89.1
## 11 LF_CTRL   d20   min          6  914   91.3
## 12 LF_PFOS   d20   min          6  881. 122.
dat.clean %>% group_by(across(all_of(PREDICTOR))) %>% get_summary_stats(!!sym(OUTCOME), type = "full")
## # A tibble: 12 × 15
##    feedtreat day   variable     n   min   max median    q1    q3   iqr   mad
##    <chr>     <chr> <fct>    <dbl> <dbl> <dbl>  <dbl> <dbl> <dbl> <dbl> <dbl>
##  1 HF_CTRL   d0    min          6   377   748   428.  404.  627  224.   58.6
##  2 HF_PFOS   d0    min          5   354   626   539   434   567  133   129. 
##  3 LF_CTRL   d0    min          6   400   721   596.  445   649  204   136. 
##  4 LF_PFOS   d0    min          6   426   758   571   523   667  144   120. 
##  5 HF_CTRL   d07   min         12   444   970   804.  762.  859   96.8  95.6
##  6 HF_PFOS   d07   min         12   456   876   702.  641   844. 202.  211. 
##  7 LF_CTRL   d07   min         12   669   993   847   802.  930  128.  128. 
##  8 LF_PFOS   d07   min         12   637   983   835   780   958. 178.  153. 
##  9 HF_CTRL   d20   min          6   750  1011   860.  788   871   83    79.3
## 10 HF_PFOS   d20   min          6   775  1008   898   875.  985  110.   98.6
## 11 LF_CTRL   d20   min          6   806  1029   893   854.  990  136.  100. 
## 12 LF_PFOS   d20   min          6   675  1018   889   844.  960. 116   110. 
## # ℹ 4 more variables: mean <dbl>, sd <dbl>, se <dbl>, ci <dbl>
# Test for outliers
dat.clean %>% 
  group_by(across(all_of(PREDICTOR))) %>% 
  identify_outliers(!!sym(OUTCOME))
## # A tibble: 3 × 11
##   feedtreat day   rat_name treatment feed  dissection  cage transit      min
##   <chr>     <chr> <chr>    <chr>     <chr> <chr>      <int> <chr>      <int>
## 1 HF_CTRL   d07   R25      CTRL      HF    d08           13 transit_7    479
## 2 HF_CTRL   d07   R30      CTRL      HF    d08           15 transit_7    444
## 3 HF_CTRL   d20   R35      CTRL      HF    d21           18 transit_20  1011
## # ℹ 2 more variables: is.outlier <lgl>, is.extreme <lgl>
# Check normality
# Build the linear model
model  <- lm(FORMULA, data = dat.clean)
# Create a QQ plot of residuals
ggqqplot(residuals(model))

# Compute Shapiro-Wilk test of normality
shapiro_test(residuals(model))
## # A tibble: 1 × 3
##   variable         statistic p.value
##   <chr>                <dbl>   <dbl>
## 1 residuals(model)     0.980   0.144
# Check the homogeneity of variances with Levene's test
dat.clean %>% levene_test(FORMULA)
## # A tibble: 1 × 4
##     df1   df2 statistic     p
##   <int> <int>     <dbl> <dbl>
## 1    11    83     0.310 0.982
# Save result
EQUAL.VAR <- dat.clean %>% levene_test(FORMULA) %>% pull(p) > 0.05

Data is normally distributed, has equal variance and three outliers, where one is critical. Therefore we can test the data with a one-way ANOVA test with Tukey’s honest significance test.

10.2 ANOVA One-Way test

Perform test

If we had equality of variance we can now run a one-way ANOVA tests anova_test() (if we have equal variance) or a welch_anova_test() (if variance vary).

if(EQUAL.VAR) {
  res.aov <- dat.clean %>% anova_test(FORMULA)
  res.aov
} else {
  res.aov <- dat.clean %>% welch_anova_test(FORMULA)
  res.aov
}
## ANOVA Table (type II tests)
## 
##          Effect DFn DFd      F        p p<.05   ges
## 1           day   2  83 47.397 1.86e-14     * 0.533
## 2     feedtreat   3  83  2.955 3.70e-02     * 0.097
## 3 day:feedtreat   6  83  0.774 5.93e-01       0.053

Perform posthoc test

if(EQUAL.VAR) {
  pwc <- dat.clean %>% tukey_hsd(FORMULA)
  pwc
} else {
  pwc <- dat.clean %>% games_howell_test(FORMULA)
  pwc
}
## # A tibble: 75 × 9
##    term          group1   group2 null.value estimate conf.low conf.high    p.adj
##  * <chr>         <chr>    <chr>       <dbl>    <dbl>    <dbl>     <dbl>    <dbl>
##  1 day           d0       d07             0   250.     173.       328.  1.87e-10
##  2 day           d0       d20             0   346.     257.       435.  1.10e-10
##  3 day           d07      d20             0    96.1     19.7      173.  9.84e- 3
##  4 feedtreat     HF_CTRL  HF_PF…          0   -24.3   -122.        73.7 9.15e- 1
##  5 feedtreat     HF_CTRL  LF_CT…          0    66.4    -30.5      163.  2.83e- 1
##  6 feedtreat     HF_CTRL  LF_PF…          0    62.4    -34.6      159.  3.37e- 1
##  7 feedtreat     HF_PFOS  LF_CT…          0    90.7     -7.26     189.  7.97e- 2
##  8 feedtreat     HF_PFOS  LF_PF…          0    86.7    -11.3      185.  1.02e- 1
##  9 feedtreat     LF_CTRL  LF_PF…          0    -4.04  -101.        92.9 1   e+ 0
## 10 day:feedtreat d0:HF_C… d07:H…          0   263.      47.3      478.  5.1 e- 3
## # ℹ 65 more rows
## # ℹ 1 more variable: p.adj.signif <chr>

Significant impact is observed on both day and grouping (feedtreat). Based on the data we see a difference between all days. We will plot this as a nested analysis using ANOVA with Tukey’s HSD on both the inner variable and the outer variable.

10.3 Create figure (feedtreat)

## Comparison for inner variable
stat.in <- dat.clean %>%
  group_by(day) %>%
  anova_test(min ~ feedtreat) %>%
  add_significance() %>%
  p_format("p", accuracy = 0.001, trailing.zero = TRUE, new.col = TRUE)
stat.in
## # A tibble: 3 × 10
##   day   Effect      DFn   DFd     F     p `p<.05`   ges p.signif p.format
## * <chr> <chr>     <dbl> <dbl> <dbl> <dbl> <chr>   <dbl> <chr>    <chr>   
## 1 d0    feedtreat     3    19 0.523 0.671 ""      0.076 ns       0.671   
## 2 d07   feedtreat     3    44 3.20  0.032 "*"     0.179 *        0.032   
## 3 d20   feedtreat     3    20 0.486 0.696 ""      0.068 ns       0.696
pwc1 <- dat.clean %>%
  group_by(day) %>%
  tukey_hsd(min ~ feedtreat) %>%
  add_significance() %>%
  p_format("p.adj", accuracy = 0.001, trailing.zero = TRUE, new.col = TRUE)
pwc1
## # A tibble: 18 × 11
##    day   term      group1  group2  null.value estimate conf.low conf.high  p.adj
##  * <chr> <chr>     <chr>   <chr>        <dbl>    <dbl>    <dbl>     <dbl>  <dbl>
##  1 d0    feedtreat HF_CTRL HF_PFOS          0    -7.5  -236.        221.  1     
##  2 d0    feedtreat HF_CTRL LF_CTRL          0    51.0  -167.        269.  0.912 
##  3 d0    feedtreat HF_CTRL LF_PFOS          0    77.2  -141.        295.  0.754 
##  4 d0    feedtreat HF_PFOS LF_CTRL          0    58.5  -170.        287.  0.888 
##  5 d0    feedtreat HF_PFOS LF_PFOS          0    84.7  -144.        313.  0.728 
##  6 d0    feedtreat LF_CTRL LF_PFOS          0    26.2  -192.        244.  0.986 
##  7 d07   feedtreat HF_CTRL HF_PFOS          0   -72.3  -221.         76.5 0.569 
##  8 d07   feedtreat HF_CTRL LF_CTRL          0    76.9   -71.9       226.  0.518 
##  9 d07   feedtreat HF_CTRL LF_PFOS          0    72.3   -76.5       221.  0.569 
## 10 d07   feedtreat HF_PFOS LF_CTRL          0   149.      0.452     298.  0.0491
## 11 d07   feedtreat HF_PFOS LF_PFOS          0   145.     -4.13      293.  0.0594
## 12 d07   feedtreat LF_CTRL LF_PFOS          0    -4.58 -153.        144.  1     
## 13 d20   feedtreat HF_CTRL HF_PFOS          0    56.8  -104.        218.  0.758 
## 14 d20   feedtreat HF_CTRL LF_CTRL          0    60.8  -100.        222.  0.719 
## 15 d20   feedtreat HF_CTRL LF_PFOS          0    27.7  -133.        189.  0.963 
## 16 d20   feedtreat HF_PFOS LF_CTRL          0     4.00 -157.        165.  1     
## 17 d20   feedtreat HF_PFOS LF_PFOS          0   -29.2  -190.        132.  0.957 
## 18 d20   feedtreat LF_CTRL LF_PFOS          0   -33.2  -194.        128.  0.938 
## # ℹ 2 more variables: p.adj.signif <chr>, p.adj.format <chr>
## Comparison for outer variable
stat.out <- dat.clean %>%
  anova_test(min ~ day) %>%
  add_significance() %>%
  p_format("p", accuracy = 0.001, trailing.zero = TRUE, new.col = TRUE)
stat.out
## ANOVA Table (type II tests)
## 
##   Effect DFn DFd      F        p p<.05   ges p.signif p.format
## 1    day   2  92 44.597 2.88e-14     * 0.492     ****   <0.001
pwc2 <- dat.clean %>%
  tukey_hsd(min ~ day) %>%
  add_significance() %>%
  p_format("p.adj", accuracy = 0.001, trailing.zero = TRUE, new.col = TRUE)
pwc2
## # A tibble: 3 × 10
##   term  group1 group2 null.value estimate conf.low conf.high    p.adj
## * <chr> <chr>  <chr>       <dbl>    <dbl>    <dbl>     <dbl>    <dbl>
## 1 day   d0     d07             0    250.     171.       329. 5.90e-10
## 2 day   d0     d20             0    346.     255.       437. 4.82e-10
## 3 day   d07    d20             0     96.1     18.0      174. 1.18e- 2
## # ℹ 2 more variables: p.adj.signif <chr>, p.adj.format <chr>
## Calculate positions statistics on plot
pwc1 <- pwc1 %>% add_xy_position(x = "day", dodge = 0.8)
pwc2 <- pwc2 %>% add_xy_position(x = "day")
pwc2$y.position <- max(pwc1$y.position)*1.1

# Create list of mean and sd for plotting
dat.sum2 <- dat.clean %>% group_by(across(all_of(c("feedtreat","day")))) %>% get_summary_stats(!!sym("min"), type = "mean_sd")

# Plot
p <- ggplot() +
  geom_crossbar(data=dat.sum2, 
                 aes(x = day, y = mean, ymin = mean, ymax = mean, color = feedtreat), 
                 linewidth=0.1, width = .7, 
                 position = position_dodge(width = 0.8)) +
  geom_errorbar(data=dat.sum2, 
                aes(x = day,y = mean, ymin = mean-sd, ymax = mean+sd, color = feedtreat),
                linewidth=0.1, width = .3, 
                position = position_dodge(width = 0.8)) +
  geom_jitter(data = dat.clean, aes(x = day, y = min, color = feedtreat), position = position_jitterdodge(jitter.width = 0.1, dodge.width = 0.8)) +
  scale_x_discrete(name = "Day", labels = c("Day 0","Day 7","Day 21")) +
  scale_y_continuous(name = "Transit time per rat (Minutes)", breaks = seq(360,1120,60)) +
  scale_color_manual(values = params$COL1, name = "Group", labels = c("HF-CTRL","HF-PFOS","LF-CTRL","LF-PFOS")) +
  theme_pubr() +
  theme(axis.title.x = element_blank())
p

p.stat <- p + stat_pvalue_manual(pwc1, tip.length = 0, hide.ns = TRUE, y.position = 1030, color = "red") +
  stat_pvalue_manual(pwc2, tip.length = 0, hide.ns = TRUE,y.position = c(1110,1140,1080))
p.stat

# Save output
suppressMessages(ggsave(filename = "plots/animal_data/transit/transit_time_feedtreat.pdf", plot = p.stat, device = "pdf", dpi = 300, units = "mm", height = 100, width = 130))
suppressMessages(ggsave(filename = "plots/animal_data/transit/transit_time_feedtreat.png", plot = p.stat, device = "png", dpi = 300, units = "mm", height = 100, width = 130))

10.4 Create figure (feed)

## Comparison for inner variable
stat.in <- dat.clean %>%
  group_by(day) %>%
  t_test(min ~ feed,
         paired = FALSE, var.equal = EQUAL.VAR, 
         detailed = TRUE, alternative = "two.sided") %>%
  add_significance() %>%
  p_format("p", accuracy = 0.001, trailing.zero = TRUE, new.col = TRUE)
stat.in
## # A tibble: 3 × 18
##   day   estimate estimate1 estimate2 .y.   group1 group2    n1    n2 statistic
## * <chr>    <dbl>     <dbl>     <dbl> <chr> <chr>  <chr>  <int> <int>     <dbl>
## 1 d0       -67.5      508.      576. min   HF     LF        11    12    -1.26 
## 2 d07     -111.       738       849. min   HF     LF        24    24    -2.82 
## 3 d20      -15.8      882.      897. min   HF     LF        12    12    -0.395
## # ℹ 8 more variables: p <dbl>, df <dbl>, conf.low <dbl>, conf.high <dbl>,
## #   method <chr>, alternative <chr>, p.signif <chr>, p.format <chr>
## Comparison for outer variable
stat.out <- dat.clean %>%
  anova_test(min ~ day) %>%
  add_significance() %>%
  p_format("p", accuracy = 0.001, trailing.zero = TRUE, new.col = TRUE)
stat.out
## ANOVA Table (type II tests)
## 
##   Effect DFn DFd      F        p p<.05   ges p.signif p.format
## 1    day   2  92 44.597 2.88e-14     * 0.492     ****   <0.001
pwc2 <- dat.clean %>%
  tukey_hsd(min ~ day) %>%
  add_significance() %>%
  p_format("p.adj", accuracy = 0.001, trailing.zero = TRUE, new.col = TRUE)
pwc2
## # A tibble: 3 × 10
##   term  group1 group2 null.value estimate conf.low conf.high    p.adj
## * <chr> <chr>  <chr>       <dbl>    <dbl>    <dbl>     <dbl>    <dbl>
## 1 day   d0     d07             0    250.     171.       329. 5.90e-10
## 2 day   d0     d20             0    346.     255.       437. 4.82e-10
## 3 day   d07    d20             0     96.1     18.0      174. 1.18e- 2
## # ℹ 2 more variables: p.adj.signif <chr>, p.adj.format <chr>
## Calculate positions statistics on plot
stat.in <- stat.in %>% add_xy_position(x = "day", dodge = 0.8)
pwc2 <- pwc2 %>% add_xy_position(x = "day")
pwc2$y.position <- max(stat.in$y.position)*1.1

# Create list of mean and sd for plotting
dat.sum2 <- dat.clean %>% group_by(across(all_of(c("feed","day")))) %>% get_summary_stats(!!sym("min"), type = "mean_sd")

# Plot
p <- ggplot() +
  geom_crossbar(data=dat.sum2, 
                 aes(x = day, y = mean, ymin = mean, ymax = mean, color = feed), 
                 linewidth=0.1, width = .7,
                 position = position_dodge(width = 0.8)) +
  geom_errorbar(data=dat.sum2, 
                aes(x = day,y = mean, ymin = mean-sd, ymax = mean+sd, color = feed),
                linewidth=0.1, width = .3,
                position = position_dodge(width = 0.8)) +
  geom_jitter(data = dat.clean, aes(x = day, y = min, color = feed), position = position_jitterdodge(jitter.width = 0.1, dodge.width = 0.8)) +
  scale_x_discrete(name = "Day", labels = c("Day 0","Day 7","Day 21")) +
  scale_y_continuous(name = "Transit time per rat (Minutes)", breaks = seq(360,1120,60)) +
  scale_color_manual(values = params$COLFEED, name = "Feed", labels = c("HF","LF")) +
  theme_pubr() +
  theme(axis.title.x = element_blank()) +
  guides(color = guide_legend(override.aes = list(size = 4, shape = 15, linetype = 0)))
p

p.stat <- p + stat_pvalue_manual(stat.in, tip.length = 0, hide.ns = TRUE, y.position = 1030, color = "red") +
  stat_pvalue_manual(pwc2, tip.length = 0, hide.ns = TRUE,y.position = c(1110,1140,1080))
p.stat

# Save output
suppressMessages(ggsave(filename = "plots/animal_data/transit/transit_time_feed.pdf", plot = p.stat, device = "pdf", dpi = 300, units = "mm", height = 100, width = 130))
suppressMessages(ggsave(filename = "plots/animal_data/transit/transit_time_feed.png", plot = p.stat, device = "png", dpi = 300, units = "mm", height = 100, width = 130))

# clear the environment and release memory
rm(list = ls(all.names = TRUE)[ls(all.names = TRUE) != "params"])
invisible(gc())

11 CONSUMPTION DATA

11.1 INTRODUCTION

Data on feed and water consumption was measured throughout the trial period (day 0 to 21) mainly with the scope of monitoring feed consumption between HF and LF groups. The two feed types, beside having high difference in soluble dietary fibre content, were not isocaloric and had different contents of fats and protein with HF diet (Altromin) providing 3340 kcal/kg and LF diet (AIN-93G based D19090404) provided 4000 kcal/kg. A significantly larger body weight gain was observed in LF fed rats by the end of the study.

The following section goes through grouped statistics on consumption data.

11.2 LOAD DATA

Loading data from CSV-format in input folder and saves as Rdata-format.

# Load analysis data
dat <- read.csv(params$cons_data, header = TRUE, sep = ";", dec = ",")

save(dat, file = "R_objects/consumption_data.Rdata")

# clear the environment and release memory
rm(list = ls(all.names = TRUE)) #will clear all objects includes hidden objects.
invisible(gc()) #free up memory and report the memory usage.

11.3 Feed per cage per day

11.3.1 Prepare data

This section sets the variables to be used and prepares the data.

# load data 
load("R_objects/consumption_data.Rdata")
params <- readRDS("R_objects/animal_params.RDS")

# Subset
dat.clean <- subset(dat, !cage == 21)

# Set names of variables
PREDICTOR <- c("feed","treatment","dissection")
OUTCOME <- "feed_cage_day_avr_g"
SUBJECT <- "cage"

# Create formula
PREDICTOR.F <- ifelse(length(PREDICTOR) > 1, paste(PREDICTOR, collapse = "*"), PREDICTOR)
FORMULA <- as.formula(paste(OUTCOME,PREDICTOR.F, sep = " ~ "))

# Summary samples in groups
dat.clean %>% group_by(across(all_of(PREDICTOR))) %>% get_summary_stats(!!sym(OUTCOME), type = "mean_sd")
## # A tibble: 8 × 7
##   feed  treatment dissection variable                n  mean    sd
##   <chr> <chr>     <chr>      <fct>               <dbl> <dbl> <dbl>
## 1 HF    CTRL      d08        feed_cage_day_avr_g     3  45.9  5.21
## 2 HF    CTRL      d21        feed_cage_day_avr_g     3  44.6  2.55
## 3 HF    PFOS      d08        feed_cage_day_avr_g     3  43.8  4.14
## 4 HF    PFOS      d21        feed_cage_day_avr_g     2  44.6  2.07
## 5 LF    CTRL      d08        feed_cage_day_avr_g     3  38.6  4.11
## 6 LF    CTRL      d21        feed_cage_day_avr_g     3  47.0  3.70
## 7 LF    PFOS      d08        feed_cage_day_avr_g     3  40.4  5.42
## 8 LF    PFOS      d21        feed_cage_day_avr_g     3  39.2  3.26
# Create plot
bxp <- dat.clean %>%
  ggboxplot(x = if_else(length(PREDICTOR) > 1, PREDICTOR[2],PREDICTOR[1]),
            y = OUTCOME,
            color = PREDICTOR[1],
            facet.by = if(length(PREDICTOR) == 3) PREDICTOR[3],
            palette = params$COL)
bxp

# Test for outliers
dat.clean %>% 
  group_by(across(all_of(PREDICTOR))) %>% 
  identify_outliers(!!sym(OUTCOME))
##  [1] feed                  treatment             dissection           
##  [4] cage                  feedtreat             feedtreatday         
##  [7] fd2                   fd6                   fd8                  
## [10] fd12                  fd16                  fd21                 
## [13] wd2                   wd4                   wd5                  
## [16] wd6                   wd8                   wd11                 
## [19] wd12                  wd15                  wd16                 
## [22] wd21                  tot_water_in          tot_water_out        
## [25] tot_water_consumption number_days           water_cage_day_avr_g 
## [28] tot_feed_in           tot_feed_out          tot_feed_consumption 
## [31] feed_cage_day_avr_g   cal_cage_day_avr_g    bw_cage_avr_g        
## [34] bw_gain_cage_avr_g    cal_bw_kg             water_bw_kg          
## [37] is.outlier            is.extreme           
## <0 rækker> (eller 0-længde row.names)
# Build the linear model
model  <- lm(FORMULA, data = dat.clean)
# Create a QQ plot of residuals
ggqqplot(residuals(model))

# Compute Shapiro-Wilk test of normality
shapiro_test(residuals(model))
## # A tibble: 1 × 3
##   variable         statistic p.value
##   <chr>                <dbl>   <dbl>
## 1 residuals(model)     0.930   0.112
plot(model, 1)

dat.clean %>% levene_test(FORMULA)
## # A tibble: 1 × 4
##     df1   df2 statistic     p
##   <int> <int>     <dbl> <dbl>
## 1     7    15     0.150 0.992
# Save result
EQUAL.VAR <- dat.clean %>% levene_test(FORMULA) %>% pull(p) > 0.05

Data presents normal distribution and equal variance.

11.3.2 PARAMETRIC test

11.3.2.1 ANOVA One-Way test

Perform test

If we had equality of variance we can now run a one-way ANOVA tests anova_test() (if we have equal variance) or a welch_anova_test() (if variance vary).

if(EQUAL.VAR) {
  res.aov <- dat.clean %>% anova_test(FORMULA)
  res.aov
} else {
  res.aov <- dat.clean %>% welch_anova_test(FORMULA)
  res.aov
}
## ANOVA Table (type II tests)
## 
##                      Effect DFn DFd     F     p p<.05   ges
## 1                      feed   1  15 3.848 0.069       0.204
## 2                 treatment   1  15 1.571 0.229       0.095
## 3                dissection   1  15 1.030 0.326       0.064
## 4            feed:treatment   1  15 0.213 0.651       0.014
## 5           feed:dissection   1  15 1.437 0.249       0.087
## 6      treatment:dissection   1  15 1.447 0.248       0.088
## 7 feed:treatment:dissection   1  15 2.963 0.106       0.165

None of the parameters show significant impact. Feed does show a trend towards an impact (p<0.1).

Perform posthoc test

A significant one-way ANOVA is generally followed up by Tukey post-hoc tests to perform multiple pairwise comparisons between groups. When running relaxed Welch one-way test, the Games-Howell post hoc test or pairwise t-tests (with no assumption of equal variances) can be used to compare all possible combinations of group differences.

if(EQUAL.VAR) {
  pwc <- dat.clean %>% tukey_hsd(FORMULA)
  pwc
} else {
  pwc <- dat.clean %>% games_howell_test(FORMULA)
  pwc
}
## # A tibble: 49 × 9
##    term            group1  group2  null.value estimate conf.low conf.high  p.adj
##  * <chr>           <chr>   <chr>        <dbl>    <dbl>    <dbl>     <dbl>  <dbl>
##  1 feed            HF      LF               0    -3.45    -7.06     0.161 0.0598
##  2 treatment       CTRL    PFOS             0    -2.11    -5.72     1.50  0.233 
##  3 dissection      d08     d21              0     1.67    -1.94     5.28  0.34  
##  4 feed:treatment  HF:CTRL LF:CTRL          0    -2.40    -9.16     4.35  0.738 
##  5 feed:treatment  HF:CTRL HF:PFOS          0    -1.06    -8.15     6.02  0.972 
##  6 feed:treatment  HF:CTRL LF:PFOS          0    -5.47   -12.2      1.29  0.135 
##  7 feed:treatment  LF:CTRL HF:PFOS          0     1.34    -5.75     8.42  0.947 
##  8 feed:treatment  LF:CTRL LF:PFOS          0    -3.06    -9.82     3.69  0.572 
##  9 feed:treatment  HF:PFOS LF:PFOS          0    -4.40   -11.5      2.68  0.315 
## 10 feed:dissection HF:d08  LF:d08           0    -5.36   -12.1      1.40  0.145 
## # ℹ 39 more rows
## # ℹ 1 more variable: p.adj.signif <chr>

None of the parameters show significant impact. Again, feed does show a trend towards an impact (p<0.1).

11.3.3 NON-PARAMETRIC test

11.3.3.1 Kruskal-Wallis test

Non-parametric method is also applied to test for due to relatively low sample count per group.

Perform test

res.kru <- dat.clean %>% kruskal_test(feed_cage_day_avr_g ~ feedtreat)
res.kru
## # A tibble: 1 × 6
##   .y.                     n statistic    df     p method        
## * <chr>               <int>     <dbl> <int> <dbl> <chr>         
## 1 feed_cage_day_avr_g    23      4.97     3 0.174 Kruskal-Wallis

Effect size

The eta squared, based on the H-statistic, can be used as the measure of the Kruskal-Wallis test effect size. The interpretation values commonly in published literature are: 0.01- < 0.06 (small effect), 0.06 - < 0.14 (moderate effect) and >= 0.14 (large effect).

dat.clean %>% kruskal_effsize(feed_cage_day_avr_g ~ feedtreat)
## # A tibble: 1 × 5
##   .y.                     n effsize method  magnitude
## * <chr>               <int>   <dbl> <chr>   <ord>    
## 1 feed_cage_day_avr_g    23   0.104 eta2[H] moderate

Post-hoc test if interaction is significant

A significant Kruskal-Wallis test is generally followed up by Dunn’s test to identify which groups are different. It’s also possible to use the Wilcoxon’s test to calculate pairwise comparisons between group levels with corrections for multiple testing.

# pairwise comparisons
pwc <- dat.clean %>% 
  dunn_test(feed_cage_day_avr_g ~ feedtreat, p.adjust.method = "fdr") 
pwc
## # A tibble: 6 × 9
##   .y.              group1 group2    n1    n2 statistic      p p.adj p.adj.signif
## * <chr>            <chr>  <chr>  <int> <int>     <dbl>  <dbl> <dbl> <chr>       
## 1 feed_cage_day_a… HF_CT… HF_PF…     6     5    -0.507 0.612  0.715 ns          
## 2 feed_cage_day_a… HF_CT… LF_CT…     6     6    -0.915 0.360  0.540 ns          
## 3 feed_cage_day_a… HF_CT… LF_PF…     6     6    -2.15  0.0316 0.189 ns          
## 4 feed_cage_day_a… HF_PF… LF_CT…     5     6    -0.365 0.715  0.715 ns          
## 5 feed_cage_day_a… HF_PF… LF_PF…     5     6    -1.54  0.123  0.369 ns          
## 6 feed_cage_day_a… LF_CT… LF_PF…     6     6    -1.23  0.217  0.434 ns

11.3.4 CONCLUSION

Neither parametric nor non-parametric testing of the dataset showed any significant effects of diet (HF or LF), treatment regime (CTRL or PFOS) or days in the trial (8 or 21, here under the variable “dissection” for dissection day). Feed does show a trend towards an impact (p<0.1).

11.3.5 Create figure (PARAMETRIC)

# Set variables for inner and outer analysis, and variable for facet
INNER.VAR <- "treatment"
OUTER.VAR <- "feed"

# Statistics costumed for facet plotting
## Pairwise comparison for inner variable
stat.in <- dat.clean %>%
  group_by(.data[[OUTER.VAR]]) %>%
  t_test(as.formula(paste(OUTCOME,"~",INNER.VAR, sep = " ")), 
         paired = FALSE, var.equal = EQUAL.VAR, 
         detailed = TRUE, alternative = "two.sided") %>%
  # adjust_pvalue(method = "bonferroni") %>%
  add_significance() %>%
  p_format("p", accuracy = 0.001, trailing.zero = TRUE, new.col = TRUE)
stat.in
## # A tibble: 2 × 18
##   feed  estimate estimate1 estimate2 .y.     group1 group2    n1    n2 statistic
## * <chr>    <dbl>     <dbl>     <dbl> <chr>   <chr>  <chr>  <int> <int>     <dbl>
## 1 HF        1.15      45.3      44.1 feed_c… CTRL   PFOS       6     5     0.546
## 2 LF        2.98      42.8      39.8 feed_c… CTRL   PFOS       6     6     1.04 
## # ℹ 8 more variables: p <dbl>, df <dbl>, conf.low <dbl>, conf.high <dbl>,
## #   method <chr>, alternative <chr>, p.signif <chr>, p.format <chr>
## Pairwise comparison for outer variable
stat.out <- dat.clean %>%
  # group_by(.data[[FACETVAR]]) %>%
  t_test(as.formula(paste(OUTCOME,"~", OUTER.VAR, sep = " ")), 
         paired = FALSE, var.equal = EQUAL.VAR, 
         detailed = TRUE, alternative = "two.sided") %>%
  # adjust_pvalue(method = "bonferroni") %>%
  add_significance() %>%
  p_format("p", accuracy = 0.001, trailing.zero = TRUE, new.col = TRUE)
stat.out
## # A tibble: 1 × 17
##   estimate estimate1 estimate2 .y.    group1 group2    n1    n2 statistic      p
## *    <dbl>     <dbl>     <dbl> <chr>  <chr>  <chr>  <int> <int>     <dbl>  <dbl>
## 1     3.45      44.7      41.3 feed_… HF     LF        11    12      1.92 0.0682
## # ℹ 7 more variables: df <dbl>, conf.low <dbl>, conf.high <dbl>, method <chr>,
## #   alternative <chr>, p.signif <chr>, p.format <chr>
## Calculate positions statistics on plot
stat.in <- stat.in %>% add_xy_position(x = OUTER.VAR, dodge = 0.8)
stat.out <- stat.out %>% add_xy_position(x = OUTER.VAR)
stat.out$y.position <- max(stat.in$y.position)*1.1

# Sort dat.clean
dat.clean <- dat.clean[order(dat.clean$feedtreat),]

# Create plot
p <- ggboxplot(dat.clean, x = OUTER.VAR, y = OUTCOME,
               color = "feedtreat",
               fill = "feedtreat", 
               add =  "jitter", 
               add.params = list(size = 1)) +
  scale_fill_manual(values = params$COL1, name = "Group", labels = c("HF_CTRL" = "HF-CTRL","HF_PFOS" = "HF-PFOS","LF_CTRL" = "LF-CTRL","LF_PFOS" = "LF-PFOS")) +
  scale_color_manual(values = c("black","black","black","black")) +
  scale_y_continuous(name = "Feed consumed per cage per day (g)",limits = c(30,60),breaks = seq(30,60,5)) +
  guides(color = "none") +
  theme(axis.title.x = element_blank())

p.feed <- p + stat_pvalue_manual(stat.in, label = "p.format",tip.length = 0, hide.ns = FALSE)+
  stat_pvalue_manual(stat.out, label = "p.format", tip.length = 0, hide.ns = FALSE)

p.feed

# Output plot
ggsave(filename = paste0("plots/animal_data/consumption/",OUTCOME,"_plot.png"), p.feed, device = "png", dpi = 300, units = "mm", width = 100, height = 100)
ggsave(filename = paste0("plots/animal_data/consumption/",OUTCOME,"_plot.pdf"), p.feed, device = "pdf", dpi = 300, units = "mm", width = 100, height = 100)

# Save output
save(p.feed, file = "plots/animal_data/consumption/feed_plots.Rdata")

# clear the environment and release memory
rm(list = ls(all.names = TRUE)) #will clear all objects includes hidden objects.
invisible(gc()) #free up memory and report the memory usage.

11.4 Calories per cage per day

Average calories per cage per day was calculated based on known caloric density of each type of feed.

11.4.1 Prepare data

This section sets the variables to be used and prepares the data.

# load data 
load("R_objects/consumption_data.Rdata")
params <- readRDS("R_objects/animal_params.RDS")

# Subset
dat.clean <- subset(dat, !cage == 21)

# Set names of variables
PREDICTOR <- c("feed","treatment","dissection")
OUTCOME <- "cal_cage_day_avr_g"
SUBJECT <- "cage"

# Create formula
PREDICTOR.F <- ifelse(length(PREDICTOR) > 1, paste(PREDICTOR, collapse = "*"), PREDICTOR)
FORMULA <- as.formula(paste(OUTCOME,PREDICTOR.F, sep = " ~ "))

# Summary samples in groups
dat.clean %>% group_by(across(all_of(PREDICTOR))) %>% get_summary_stats(!!sym(OUTCOME), type = "mean_sd")
## # A tibble: 8 × 7
##   feed  treatment dissection variable               n  mean    sd
##   <chr> <chr>     <chr>      <fct>              <dbl> <dbl> <dbl>
## 1 HF    CTRL      d08        cal_cage_day_avr_g     3  153. 17.4 
## 2 HF    CTRL      d21        cal_cage_day_avr_g     3  149.  8.51
## 3 HF    PFOS      d08        cal_cage_day_avr_g     3  146. 13.8 
## 4 HF    PFOS      d21        cal_cage_day_avr_g     2  149.  6.93
## 5 LF    CTRL      d08        cal_cage_day_avr_g     3  154. 16.4 
## 6 LF    CTRL      d21        cal_cage_day_avr_g     3  188. 14.8 
## 7 LF    PFOS      d08        cal_cage_day_avr_g     3  162. 21.7 
## 8 LF    PFOS      d21        cal_cage_day_avr_g     3  157. 13.1
# Create plot
bxp <- dat.clean %>%
  ggboxplot(x = if_else(length(PREDICTOR) > 1, PREDICTOR[2],PREDICTOR[1]),
            y = OUTCOME,
            color = PREDICTOR[1],
            facet.by = if(length(PREDICTOR) == 3) PREDICTOR[3],
            palette = params$COL)
bxp

# Test for outliers
dat.clean %>% 
  group_by(across(all_of(PREDICTOR))) %>% 
  identify_outliers(!!sym(OUTCOME))
##  [1] feed                  treatment             dissection           
##  [4] cage                  feedtreat             feedtreatday         
##  [7] fd2                   fd6                   fd8                  
## [10] fd12                  fd16                  fd21                 
## [13] wd2                   wd4                   wd5                  
## [16] wd6                   wd8                   wd11                 
## [19] wd12                  wd15                  wd16                 
## [22] wd21                  tot_water_in          tot_water_out        
## [25] tot_water_consumption number_days           water_cage_day_avr_g 
## [28] tot_feed_in           tot_feed_out          tot_feed_consumption 
## [31] feed_cage_day_avr_g   cal_cage_day_avr_g    bw_cage_avr_g        
## [34] bw_gain_cage_avr_g    cal_bw_kg             water_bw_kg          
## [37] is.outlier            is.extreme           
## <0 rækker> (eller 0-længde row.names)
# Build the linear model
model  <- lm(FORMULA, data = dat.clean)
# Create a QQ plot of residuals
ggqqplot(residuals(model))

# Compute Shapiro-Wilk test of normality
shapiro_test(residuals(model))
## # A tibble: 1 × 3
##   variable         statistic p.value
##   <chr>                <dbl>   <dbl>
## 1 residuals(model)     0.946   0.239
plot(model, 1)

dat.clean %>% levene_test(FORMULA)
## # A tibble: 1 × 4
##     df1   df2 statistic     p
##   <int> <int>     <dbl> <dbl>
## 1     7    15     0.194 0.982
# Save result
EQUAL.VAR <- dat.clean %>% levene_test(FORMULA) %>% pull(p) > 0.05

Data presents normal distribution and equal variance.

11.4.2 PARAMETRIC test

11.4.2.1 ANOVA One-Way test

Perform test

If we had equality of variance we can now run a one-way ANOVA tests anova_test() (if we have equal variance) or a welch_anova_test() (if variance vary).

if(EQUAL.VAR) {
  res.aov <- dat.clean %>% anova_test(FORMULA)
  res.aov
} else {
  res.aov <- dat.clean %>% welch_anova_test(FORMULA)
  res.aov
}
## ANOVA Table (type II tests)
## 
##                      Effect DFn DFd     F     p p<.05   ges
## 1                      feed   1  15 6.504 0.022     * 0.302
## 2                 treatment   1  15 1.652 0.218       0.099
## 3                dissection   1  15 1.217 0.287       0.075
## 4            feed:treatment   1  15 0.307 0.588       0.020
## 5           feed:dissection   1  15 1.618 0.223       0.097
## 6      treatment:dissection   1  15 1.799 0.200       0.107
## 7 feed:treatment:dissection   1  15 3.205 0.094       0.176

Feed show significant impact (p<0.05).

Perform posthoc test

A significant one-way ANOVA is generally followed up by Tukey post-hoc tests to perform multiple pairwise comparisons between groups. When running relaxed Welch one-way test, the Games-Howell post hoc test or pairwise t-tests (with no assumption of equal variances) can be used to compare all possible combinations of group differences.

if(EQUAL.VAR) {
  pwc <- dat.clean %>% tukey_hsd(FORMULA)
  pwc
} else {
  pwc <- dat.clean %>% games_howell_test(FORMULA)
  pwc
}
## # A tibble: 49 × 9
##    term            group1  group2  null.value estimate conf.low conf.high  p.adj
##  * <chr>           <chr>   <chr>        <dbl>    <dbl>    <dbl>     <dbl>  <dbl>
##  1 feed            HF      LF               0    15.7      2.24     29.2  0.0252
##  2 treatment       CTRL    PFOS             0    -8.07   -21.5       5.41 0.221 
##  3 dissection      d08     d21              0     6.75    -6.73     20.2  0.303 
##  4 feed:treatment  HF:CTRL LF:CTRL          0    20.3     -4.94     45.5  0.138 
##  5 feed:treatment  HF:CTRL HF:PFOS          0    -3.49   -29.9      22.9  0.981 
##  6 feed:treatment  HF:CTRL LF:PFOS          0     8.00   -17.2      33.2  0.797 
##  7 feed:treatment  LF:CTRL HF:PFOS          0   -23.8    -50.2       2.68 0.0856
##  8 feed:treatment  LF:CTRL LF:PFOS          0   -12.3    -37.5      12.9  0.517 
##  9 feed:treatment  HF:PFOS LF:PFOS          0    11.5    -14.9      37.9  0.605 
## 10 feed:dissection HF:d08  LF:d08           0     8.18   -17.0      33.4  0.787 
## # ℹ 39 more rows
## # ℹ 1 more variable: p.adj.signif <chr>

Feed show significant impact (p<0.05).

11.4.3 NON-PARAMETRIC test

Non-parametric method is also applied to test for due to relatively low sample count per group.

11.4.3.1 Kruskal-Wallis test

Perform test

res.kru <- dat.clean %>% kruskal_test(cal_cage_day_avr_g ~ feed)
res.kru
## # A tibble: 1 × 6
##   .y.                    n statistic    df      p method        
## * <chr>              <int>     <dbl> <int>  <dbl> <chr>         
## 1 cal_cage_day_avr_g    23      4.13     1 0.0422 Kruskal-Wallis

Effect size

The eta squared, based on the H-statistic, can be used as the measure of the Kruskal-Wallis test effect size. The interpretation values commonly in published literature are: 0.01- < 0.06 (small effect), 0.06 - < 0.14 (moderate effect) and >= 0.14 (large effect).

dat.clean %>% kruskal_effsize(cal_cage_day_avr_g ~ feed)
## # A tibble: 1 × 5
##   .y.                    n effsize method  magnitude
## * <chr>              <int>   <dbl> <chr>   <ord>    
## 1 cal_cage_day_avr_g    23   0.149 eta2[H] large

Post-hoc test if interaction is significant

A significant Kruskal-Wallis test is generally followed up by Dunn’s test to identify which groups are different. It’s also possible to use the Wilcoxon’s test to calculate pairwise comparisons between group levels with corrections for multiple testing.

# pairwise comparisons
pwc <- dat.clean %>% 
  dunn_test(cal_cage_day_avr_g ~ feed, p.adjust.method = "fdr") 
pwc
## # A tibble: 1 × 9
##   .y.             group1 group2    n1    n2 statistic      p  p.adj p.adj.signif
## * <chr>           <chr>  <chr>  <int> <int>     <dbl>  <dbl>  <dbl> <chr>       
## 1 cal_cage_day_a… HF     LF        11    12      2.03 0.0422 0.0422 *

11.4.4 CONCLUSION

Both ANOVA and Kruskal-Wallis testing showed significant effects of feed (HF or LF) as the only factor (p<0.05). Therefore is can be concluded that the caloric intake in the LF group was significantly different from the HF group, despite the difference in amount of food consumed were insignificant between the two feed groups.

11.4.5 Create figure (PARAMETRIC)

# Set variables for inner and outer analysis, and variable for facet
INNER.VAR <- "treatment"
OUTER.VAR <- "feed"

# Statistics costumed for facet plotting
## Pairwise comparison for inner variable
stat.in <- dat.clean %>%
  group_by(.data[[OUTER.VAR]]) %>%
  t_test(as.formula(paste(OUTCOME,"~",INNER.VAR, sep = " ")), 
         paired = FALSE, var.equal = EQUAL.VAR, 
         detailed = TRUE, alternative = "two.sided") %>%
  # adjust_pvalue(method = "bonferroni") %>%
  add_significance() %>%
  p_format("p", accuracy = 0.001, trailing.zero = TRUE, new.col = TRUE)
stat.in
## # A tibble: 2 × 18
##   feed  estimate estimate1 estimate2 .y.     group1 group2    n1    n2 statistic
## * <chr>    <dbl>     <dbl>     <dbl> <chr>   <chr>  <chr>  <int> <int>     <dbl>
## 1 HF        3.84      151.      147. cal_ca… CTRL   PFOS       6     5     0.546
## 2 LF       11.9       171.      159. cal_ca… CTRL   PFOS       6     6     1.04 
## # ℹ 8 more variables: p <dbl>, df <dbl>, conf.low <dbl>, conf.high <dbl>,
## #   method <chr>, alternative <chr>, p.signif <chr>, p.format <chr>
## Pairwise comparison for outer variable
stat.out <- dat.clean %>%
  # group_by(.data[[FACETVAR]]) %>%
  t_test(as.formula(paste(OUTCOME,"~", OUTER.VAR, sep = " ")), 
         paired = FALSE, var.equal = EQUAL.VAR, 
         detailed = TRUE, alternative = "two.sided") %>%
  # adjust_pvalue(method = "bonferroni") %>%
  add_significance() %>%
  p_format("p", accuracy = 0.001, trailing.zero = TRUE, new.col = TRUE)
stat.out
## # A tibble: 1 × 17
##   estimate estimate1 estimate2 .y.    group1 group2    n1    n2 statistic      p
## *    <dbl>     <dbl>     <dbl> <chr>  <chr>  <chr>  <int> <int>     <dbl>  <dbl>
## 1    -15.7      149.      165. cal_c… HF     LF        11    12     -2.29 0.0323
## # ℹ 7 more variables: df <dbl>, conf.low <dbl>, conf.high <dbl>, method <chr>,
## #   alternative <chr>, p.signif <chr>, p.format <chr>
## Calculate positions statistics on plot
stat.in <- stat.in %>% add_xy_position(x = OUTER.VAR, dodge = 0.8)
stat.out <- stat.out %>% add_xy_position(x = OUTER.VAR)
stat.out$y.position <- max(stat.in$y.position)*1.1

# Sort dat.clean
dat.clean <- dat.clean[order(dat.clean$feedtreat),]

# Create plot
p <- ggboxplot(dat.clean, x = OUTER.VAR, y = OUTCOME,
               color = "feedtreat",
               fill = "feedtreat", 
               add =  "jitter", 
               add.params = list(size = 1)) +
  scale_fill_manual(values = params$COL1, name = "Group", labels = c("HF_CTRL" = "HF-CTRL","HF_PFOS" = "HF-PFOS","LF_CTRL" = "LF-CTRL","LF_PFOS" = "LF-PFOS")) +
  scale_color_manual(values = c("black","black","black","black")) +
  scale_y_continuous(name = "Kcal intake per cage per day (kcal)", limits = c(120,240),breaks = seq(120,240,20)) +
  guides(color = "none") +
  theme(axis.title.x = element_blank())

p.cal <- p + stat_pvalue_manual(stat.in, label = "p.format",tip.length = 0, hide.ns = FALSE)+
  stat_pvalue_manual(stat.out, label = "p.format", tip.length = 0, hide.ns = FALSE, color = "red")

p.cal

# Output plot
ggsave(filename = paste0("plots/animal_data/consumption/",OUTCOME,"_plot.png"), p.cal, device = "png", dpi = 300, units = "mm", width = 100, height = 100)
ggsave(filename = paste0("plots/animal_data/consumption/",OUTCOME,"_plot.pdf"), p.cal, device = "pdf", dpi = 300, units = "mm", width = 100, height = 100)

save(p.cal, file = "plots/animal_data/consumption/calorie_plots.Rdata")

# clear the environment and release memory
rm(list = ls(all.names = TRUE)) #will clear all objects includes hidden objects.
invisible(gc()) #free up memory and report the memory usage.

11.5 Combined plot

# Load saved plots
load("plots/animal_data/consumption/calorie_plots.Rdata")
load("plots/animal_data/consumption/feed_plots.Rdata")

# Combined plots
p <- ggarrange(p.feed, p.cal,
               ncol = 2, nrow = 1,
               common.legend = TRUE,
               widths = c(1,1))
p

# Output plot
ggsave(filename = "plots/figures/final_figures_Appendix/revision_-_consumption.png", p, device = "png", dpi = 300, units = "mm", width = 150, height = 80)
ggsave(filename = "plots/figures/final_figures_Appendix/revision_-_consumption.pdf", p, device = "pdf", dpi = 300, units = "mm", width = 150, height = 80)

# clear the environment and release memory
rm(list = ls(all.names = TRUE)) #will clear all objects includes hidden objects.
invisible(gc()) #free up memory and report the memory usage.

12 SETTINGS

Overview of the parameters and packages that were used for this analysis

12.1 PARAMETERS

The following paramenters were set in for this analysis:

params <- readRDS("R_objects/animal_params.RDS")

tmp <- unlist(params)
dat <- data.frame(Parameter = names(tmp), Value = unname(tmp))


kbl(dat, row.names = F) %>% kable_classic(lightable_options = "striped")
Parameter Value
input input/fibrex_data_R.csv
isomer_data input/pfos_isomer_data.csv
cons_data input/consumption_data.csv
COL0.CTRL #eeeeee
COL0.PFOS #666666
COL1.LF_CTRL #a5cee3
COL1.LF_PFOS #1778b6
COL1.HF_CTRL #b4d88a
COL1.HF_PFOS #30a148
COL2.LF_CTRL_d8 #a5cee3
COL2.LF_CTRL_d21 #a5cee3
COL2.LF_PFOS_d8 #1778b6
COL2.LF_PFOS_d21 #1778b6
COL2.HF_CTRL_d8 #b4d88a
COL2.HF_CTRL_d21 #b4d88a
COL2.HF_PFOS_d8 #30a148
COL2.HF_PFOS_d21 #30a148
COLFEEDDAY.d08_HF #d2e9cc
COLFEEDDAY.d12_HF #9fd3a3
COLFEEDDAY.d16_HF #65bf76
COLFEEDDAY.d21_HF #32a248
COLFEEDDAY.d08_LF #bde7fb
COLFEEDDAY.d12_LF #86c1e5
COLFEEDDAY.d16_LF #509cce
COLFEEDDAY.d21_LF #1879b7
COLFEED.LF #1778b6
COLFEED.HF #30a148
COL_BORDER.d08 #770000
COL_BORDER.d12 #505050
COL_BORDER.d16 #cc9900
COL_BORDER.d21 #000000
COL_4BLACK1 #000000
COL_4BLACK2 #000000
COL_4BLACK3 #000000
COL_4BLACK4 #000000

12.2 PACKAGES

The analysis was run in R version 4.3.1 using the following packages:

pack <- data.frame(Package = (.packages()))

for (i in seq(nrow(pack))) pack$Version[i] <- as.character(packageVersion(pack$Package[i]))

kbl(pack[order(pack$Package),], row.names = F) %>% kable_classic(lightable_options = "striped")   
Package Version
ape 5.8
base 4.3.1
cowplot 1.1.3
datasets 4.3.1
DAtest 2.8.0
decontam 1.22.0
dplyr 1.1.4
forcats 1.0.0
ggplot2 3.5.1
ggpubr 0.6.0
ggrepel 0.9.5
graphics 4.3.1
grDevices 4.3.1
kableExtra 1.4.0
lattice 0.21.8
lubridate 1.9.3
methods 4.3.1
pals 1.9
permute 0.9.7
phangorn 2.11.1
phyloseq 1.46.0
plotly 4.10.4
purrr 1.0.2
readr 2.1.5
rstatix 0.7.2
stats 4.3.1
stringr 1.5.1
tibble 3.2.1
tidyr 1.3.1
tidyverse 2.0.0
utils 4.3.1
vegan 2.6.6.1